
Содержание
Бьeрн Страустрап. Справочное руководство по C++


---------------------------------------------------------------
 Второе дополненное издание
---------------------------------------------------------------

 * СПРАВОЧНОЕ РУКОВОДСТВО


R.1 Введение


Это справочное руководство описывает язык программирования С++ по
состоянии на май 1991. С++ - язык программирования общего назначения,
базирующийся на языке программирования С Ь.

Ь  "The C Programming Language" B. Kernighan, D. Ritchie. Prentice
   Hall, 1978, 1988.
   Есть русский перевод: "Язык программирования С. Задачи по языку С"
   Б. Керниган, Д. Ритчи, А. Фьюер. "Финансы и статистика". 1984

В дополнение к возможностям С язык С++ предоставляет классы, функции
подстановки, перегрузку операций, перегрузку имен функций, постоянные
типы, ссылки, операторы управления свободной памятью, проверку
параметров функций и приведение типов. Все расширения С суммируются
в $$R.18.1. Различия между С++ и ANSI C++ приведены в $$R.18.2 Ь.

Ь  American National Standard X3.159-1989. Американский национальный
   стандарт.

Расширения  С++ версии 1985 года до данного описания суммируются в
$$R.18.1.2. Разделы, относящиеся к шаблонам типа ($$R.14) и
управлению особыми ситуациями ($$R.15), являются местами планируемых
расширений языка.

R.1.1 Обзор


Это руководство содержит следующее:
1. Введение.
2. Соглашения о лексических понятиях.
3. Основные понятия.
4. Стандартные преобразования.
5. Выражения.
6. Операторы.
7. Описания.
8. Описатели.
9. Классы.
10. Производные классы.
11. Контроль доступа к членам.
12. Специальные функции-члены.
13. Перегрузка.
14. Шаблоны типов.
15. Управление особыми ситуациями.
16. Препроцессорная обработка.
    Приложение A: Сводка синтаксиса
    Приложение B: Совместимость

R.1.2 Запись синтаксиса


В записи синтаксиса языка в этом руководстве синтаксические понятия
обозначаются курсивом, а литеральные слова и символы шрифтом постоянной
ширины. Варианты перечисляются на отдельных строках, за исключением
тех немногих случаев, когда длинный список вариантов дается на одной
строке с пометкой "один из". Необязательный терминальный или
нетерминальный символ обозначается с помощью нижнего индекса "opt",
поэтому

   { выражение opt }

означает необязательное выражение, заключенное в фигурные скобки.

R.2 Соглашения о лексических понятиях


Программа на С++ состоит из одного или нескольких файлов ($$R.3.3).
С логической точки зрения файл транслируется за несколько проходов.
Первый проход состоит в препроцессорной обработке ($$R.16), на которой
происходит включение файлов и макроподстановка. Работа препроцессора
управляется с помощью команд, являющихся строками, первый символ
которых отличный от пробела есть # ($$R2.1). Результат работы
препроцессора есть последовательность лексем. Такую последовательность
лексем, т.е. файл после препроцессорной обработки, называют
единицей трансляции.

R.2.1 Лексемы


Существуют лексемы пяти видов: идентификаторы, служебные слова,
литералы, операции и различные разделители. Пробелы, вертикальная
и горизонтальная табуляция, конец строки, перевод строки и комментарии
(все вместе "обобщенные" пробелы), как указано ниже, игнорируются,
за исключением того, что они отделяют лексемы. Обобщенные пробелы
нужны, чтобы разделить стоящие рядом идентификаторы, служебные
слова и константы.
   Если входной поток разобран на лексемы до данного символа, то
следующей лексемой считается лексема с максимально возможной длиной,
которая начинается с этого символа.

R.2.2


Комментарии
Символы /* начинают комментарий, который завершается символами */.
Такие комментарии не могут быть вложенными. Символы // начинают
комментарий, который завершается концом этой строки. Символы //,
/* и */ не имеют специального назначения в комментарии // и
рассматриваются как обычные символы. Аналогично символы // и /*
не имеют специального назначения внутри комментария /*.

R.2.3 Идентификаторы


Идентификатор - это последовательность букв и цифр произвольной длины.
Первый символ должен быть буквой, символ подчеркивания _ считается
буквой. Прописные и строчные буквы различаются. Все символы
существенны.

R.2.4 Служебные слова


Перечисленные ниже идентификаторы фиксируются как служебные слова и
в другом смысле не могут использоваться:

 asm        continue     float    new          signed      try
 auto       default      for      operator     sizeof      typedef
 break      delete       friend   private      static      union
 case       do           goto     protected    struct      unsigned
 catch      double       if       public       switch      virtual
 char       else         inline   register     template    void
 class      enum         int      return       this        volatile
 const      extern       long     short        throw       while

В дополнение к этому идентификаторы, содержащие двойное подчеркивание
(__) резервируются для реализаций С++ и стандартных библиотек и
пользователи не должны употреблять их.
   В представлении программы на С++ в кодировке ASCII используются
в качестве операций или разделителей следующие символы:

  !  %  ^  &   *  (  )  -  +  =  {  }  |  ~
  [  ]  \  ;   '  :  "  <  >  ?   , .  /

а следующие комбинации символов используются для задания операций:

  ->  ++  --  .*  ->*  <<  >>   <=  >=  ==  !=  &&
  ||  *=  /=  %=  +=   -=  <<=  >>= &=  ^=  |=  ::

Каждая операция считается отдельной лексемой.
   В дополнении к этому следующие символы резервируются для препроцессора:

   #  ##

  Определенные, зависящие от реализации, свойства, такие как
тип операции sizeof ($$R5.3.2) или диапазоны базовых типов
($$R.3.6.1) определяются в стандартных заголовочных файлах
($$R.16.4)

      <float.h>   <limits.h>  <stddef.h>

Эти файлы являются частью ANSI стандарта для С. Кроме того
заголовочные файлы

      <new.h>    <stdarg.h>   <stdlib.h>

определяют типы самых важных библиотечных функций. Два последних
файла входят в ANSI стандарт для С, файл <new.h> относится только
к С++.

R.2.5 Литералы


Есть несколько видов литералов (часто называемых "константами").
    литерал:
              целая константа
              символьная константа
              константа с плавающей точкой
              строка литералов

R.2.5.1 Целые константы


Все целые константы, состоящие из последовательности цифр, считаются
десятичными (основание счисления десять), если только они не начинаются
с 0 (цифра ноль). Последовательность цифр, начинающаяся с 0,
считается восьмеричным целым (основание счисления восемь). Цифры 8 и 9
не являются восьмеричными. Последовательность цифр, начинающаяся с
0x или 0X, считается шестнадцатеричным целым (основание счисления
шестнадцать). Шестандцатеричные цифры могут состоять из символов
от a или A до f или F с десятичными значениями их от десяти до
пятнадцати. Например, число двенадцать можно записать как 12,
014 или 0XC.
     Тип целой константы определяется ее представлением, значением и
окончанием. Если она десятичная и не имеет окончания, ее тип будет
первым подходящим для ее значения из следующих типов: int, long int,
unsigned long int. Если она восьмеричная или шестнадцатеричная и
не имеет окончания, ее тип будет первым подходящим для ее значения
из следующих: int, unsigned int, long int, unsigned long int.
Если она оканчивается символом u или U, ее тип будет первым подходящим
для ее значения из следующих: unsigned int, unsigned long int. Если
она оканчивается символом l или L, ее тип будет первым подходящим
для ее значения из следующих: long int, unsigned long int. Если
она оканчивается на ul, lu, uL, Lu, Ul, lU, UL или LU, ее типом
будет unsigned long int.

R.2.5.2 Символьные константы


Символьной константой является один или несколько символов, заключенные
в одиночные кавычки, например 'x'. Константа из одного символа имеет
тип char. Значение константы из одного символа есть порядковый номер
символа в таблице кодировки символов на данной машине. Символьные
константы из нескольких символов имеют тип int. Значение такой
константы зависит от реализации.
    Некоторые символы, не имеющие графического представления, как
одиночная кавычка ',двойная кавычка ", знак вопроса ?, обратная
дробная черта \, можно представлять комбинацией символов (начинающейся
с \) в соответствии с приводимой ниже таблицей:

     конец строки                  NL (LF)  \n
     горизонтальная табуляция      HT       \t
     вертикальная табуляция        VT       \v
     шаг назад                     BS       \b
     возврат каретки               CR       \r
     перевод формата (авторегистр) FF       \f
     сигнал                        BEL      \a
     обратная дробная черта        \        \\
     знак вопроса                  ?        \?
     одиночная кавычка             '        \'
     двойная кавычка               "        \"
     восьмеричное число            ooo      \ooo
     шестнадцатеричное число       hhh      \xhhh

Если за обратной дробной чертой следует символ, отличный от
перечисленных, результат неопределен.
Комбинация \ooo состоит из обратной дробной черты, а которой
следуют одна, две или три восьмеричные цифры. Считается, что они
определяют значение искомого символа. Комбинация \xhhh состоит из
из обратной дробной черты, за которой следует x, а за ним, в свою
очередь, следует последовательность шестнадцатеричных цифр.
Считается, что она задает значение искомого символа. Нет ограничения
на длину  этой  последовательности шестнадцатеричных цифр.
Последовательность восьмеричных или шестнадцатеричных цифр
оканчивается, когда встречается первый символ, который не есть
восьмеричная или шестнадцатеричная цифра соответственно. Если
значение символьной константы превосходит максимальное из char,
то оно определяется реализацией.
    Символьная константа, которой непосредственно предшествует
буква L, является широкой символьной константой, например, L'ab'.
Такие константы имеют тип wchar_t, являющийся целочисленным типом
($$R.3.6.1), определенном в стандартном заголовочном файле
<stddef.h>. Широкие символы предназначены для такого набора
символов, где значение символа не помещается в один байт.

R.2.5.3 Константы с плавающей точкой


Константы с плавающей точкой состоят из целой части, символа
точка, дробной части, e или E, целого показателя с возможным
знаком и возможным окончанием, указывающим тип. Целая и дробная
части состоят из последовательности десятичных (основание
счисления десять) цифр. Или целая часть, или дробная часть
(но не обе) могут отсутствовать. Или точка, или символ e (или E)
вместе с показателем могут отсутствовать (но не оба). Тип
константы с плавающей точкой есть double, если только он не
задан явно с помощью окончания. Окончания f или F задают тип
float, окончания l или L задают тип long double.

R.2.5.4 Строки литералов


Строка литералов есть последовательность символов  (как они определены
в $$R.2.5.2), заключенная в двойные кавычки, т.е. "...". Строка
имеет тип "массив символов" и класс памяти static ($$R.3.5), она
инициализируется заданными символами. Будут ли все строки различны
(т.е. хранится в отдельных объектах), определяется реализацией.
    Соседние строки литералов конкатенируются. Символы в строках,
полученных при конкатенации, хранятся отдельно. Например, после
конкатенации

    "\xA"  "B"

строка будет содержать два символа '\xA' и 'B' (а не один
шестнадцатеричный символ '\xAB').
     После всех необходимых конкатенаций к строке добавляется
символ '\0', чтобы программа, читающая строку, могла определить
ее конец. Размер строки равен числу всех ее символов, включая
символ завершитель строки. Внутри строки перед символом двойной
кавычки " должен идти символ \.
    Строка литералов, перед которой непосредственно идет символ L,
считается широкосимвольной строкой, например, L"asdf". Такая
строка имеет тип "массив элементов типа wchar_t", где wchar_t
целочисленный тип, определенный в стандартном заголовочном файле
<stddef.h>. Результат конкатенации обычных и широкосимвольных
строк литералов неопределен.

R.3 Основные понятия


Имя обозначает объект, функцию, множество функций, элемент
перечисления, тип, член класса, шаблон типа, значение или метку.
Имя становится известно в программе с помощью описания. Имя можно
использовать только в пределах части программы, называемой областью
видимости имени. Имя имеет тип, который задает его использование.
Имя, используемое в более чем одной единице трансляции, может
обозначать один и тот же (а может и разные) объект, функцию, тип,
шаблон типа или значение, в зависимости от компоновки ($$R.3.3)
этих единиц трансляции.
    Объект имеет область для его хранения ($$R.3.7). Поименованный
объект имеет класс памяти ($$R.3.5), который определяет его время
жизни. Интерпретация значений, хранящихся в объекте, определяется
типом выражения, задающего доступ к объекту.

R.3.1 Описания и определения


Описание ($$r.7) делает известным в программе одно или несколько
имен. Описание считается определением, если только оно не описывает
функцию без задания ее тела ($$R.8.3), не содержит описателя
extern ($$R.7.11), не имеет части инициализации или тела функции,
не является описанием статического члена данных в описании класса
($$R.9.4), не является описанием имени класса ($$R.9.1), не является
описанием typedef ($$R.7.1.3). Ниже приведены примеры определений:

     int a;
     extern const c = 1;
     int f(int x) { return x+a; }
     struct S { int a; int b; };
     enum { up, down };

тогда как ниже следуют только описания:

     extern int a;
     extern const c;
     int f(int);
     struct S;
     typedef int Int;

   Для каждого объекта, функции, класса и элемента перечисления,
используемых в программе, должно быть только одно определение
($$R.3.3). Если функция никогда не вызывается и ее адрес никогда
не используется, ее не нужно определять. Аналогично, если имя класса
используется только так, что не требуется знать определения класса,
то такой класс не надо определять.

R.3.2 Область видимости


Существует четыре области видимости: локальная, функция, файл и класс.
    Локальная: Имя, описанное в блоке ($$R.6.3), является локальным
    в этом блоке и может использоваться только в нем и в блоках,
    содержащихся в этом блоке и появляющихся после момента описания.
    Имена формальных параметров рассматриваются, как если бы они были
    описаны в самом объемлющем блоке этой функции.
    Функция: Метки ($$R.6.1) можно использовать повсюду в функции,
    в которой они описаны. Только метки имеют область видимости,
    совпадающую с функцией.
    Файл: Имя описанное вне всех блоков ($$R.6.3) и классов ($$R.9)
    имеет область видимости файл и может быть использовано в единице
    трансляции, в которой оно появляется после момента описания. Имена,
    описанные с файловой областью видимости, называются глобальными.
    Класс: Имя члена класса является локальным в своем классе и
    оно может быть использовано только в функции-члене этого класса
    ($$R.9.3), или после операции . , применяемой к объекту данного
    класса ($$R.5.2.4) или объекту производного  класса($$R.10),
    или после операции ->, применяемой к указателю на объект данного
    класса ($$R.5.2.4) или на объект производного класса, или после
    операции разрешения :: ($$R.5.1), примененной к имени данного
    или производного класса. Имя, введенное с помощью операции
   friend ($$R.11.4), принадлежит той же области определенности,
   что и класс, содержащий описание friend. Класс, впервые
   описанный в операторе return или в типе параметра, принадлежит
   к глобальной области видимости.
Специальные соглашения действуют на имена, введенные при описании
параметров функции ($$R.8.2.5) и в описаниях friend ($$R.11.4).
     Имя может быть скрыто явным описанием того же имени в объемлющем
блоке или классе. Скрытое имя члена класса все-таки можно
использовать, если оно предваряется именем класса, к которому
применена операция :: ($$R.4.1, $$R.9.4, $$R.10). Скрытое имя объекта,
функции, типа или элемента перечисления с файловой областью видимости
можно использовать, если оно предваряется унарной операцией ::
($$R.5.1). В дополнении к этому, имя класса ($$R.9.1) может быть
скрыто именем объекта, функции или элемента перечисления, имеющего
ту же область видимости. Если класс и объект, или функция, или
элемент перечисления описаны (в любом порядке) с одинаковым именем
в одной области видимости, то имя класса становится скрытым. Имя
класса, скрытое в локальной области видимости или в области
видимости класса именем объекта, функции или элемента перечисления,
все-таки можно использовать, если предварить его подходящей
спецификацией class, struct или union ($$R.7.1.6). Аналогично,
скрытое имя элемента перечисления можно использовать, если
предварить его спецификацией типа enum ($$R.7.1.6). В $$R.10.4
приводится сводка правил области видимости.
Моментом описания имени считается момент завершения описателя имени
($$R.8), предшествующей части инициализации (если она есть).
Например,

    int x = 12;
    { int x = x; }

Здесь второе x инициализируется своим собственным (неопределенным)
значением.
   Моментом описания элемента перечисления считается момент сразу
после появления его идентификатора, например:

    enum { x = x };

Здесь элемент перечисления x опять инициализируется своим собственным
(неопределенным) значением.

R.3.3 Программа и связывание


Программа состоит из одного или нескольких файлов, связываемых вместе
($$R.2). Файл состоит из последовательности описаний.
    Имя с файловой областью видимости, которое явно описано как
static, является локальным в своей единице трансляции и может
использоваться для именования объектов, функций и т.п. в других
единицах трансляции. Говорят, что такие имена имеют внутреннее
связывание. Имя с файловой областью видимости, которое явно описано
со спецификацией inline, является локальным в своей единице
трансляции. Имя с файловой областью видимости, которое явно описано
со спецификацией const и не описано явно как extern, считается
локальным в своей единице трансляции. То же верно для имени класса,
которое не использовалось в нелокальных для данной единицы
трансляции описаниях объекта, функции  или класса, и который не
имеет статических членов ($$R.9.4), не имеет функций-членов, кроме
подстановок ($$R.9.3.2). Всякое описание некоторого имени с
файловой областью видимости, которое не описано одним из перечисленных
способов так, чтобы иметь внутреннее связывание, в многофайловой
программе обозначает один и тот же объект ($$R.3.7), функцию
($$R.8.2.5) или класс ($$R.9). Такие имена называются внешними или
говорят, что они имеют внешнее связывание. В частности, поскольку
нельзя описать имя класса как static, всякое употребление имени
некоторого класса с файловой областью видимости, который (класс)
использовался для описания объекта или функции с внешним связыванием,
или же который имеет статический член или функцию-член,
не являющуюся подстановкой, будет обозначать один и тот же класс.
   Имена определяемых типов (typedef $$R.7.13), элементы перечисления
($$R.7.2) или имена шаблонов типа ($$R.14) не имеют внешнего
связывания.
   Статические члены класса ($$R.9.4) допускают внешнее связывание.
   Функции-члены, не являющиеся подстановкой, допускают внешнее
связывание. Функции-члены, являющиеся подстановкой, должны иметь
в точности одно определение в программе.
   Локальные имена ($$R.3.2), явно описанные со спецификацией
extern, имеют внешнее связывание, если только они уже не были
описаны как static ($$R.7.1.1).
   Типы, используемые во всех описаниях некоторого внешнего имени,
должны совпадать, за исключением использования имен определяемых
типов ($$R.7.1.3) и указания границ массивов ($$R.8.2.4).
Должно быть в точности одно определение для каждой функции, объекта,
класса и элемента перечисления, используемых в программе. Однако,
если функция никогда не вызывается и ее адрес никогда не используется,
ее не нужно определять. Аналогично, если имя класса используется
только таким образом, что не требуется знать определение класса,
то класс не нужно определять.
   Область видимости функции может быть только файл или класс.
   С помощью спецификации связывания можно добиться связывания с
описаниями на другом языке ($$R.7.4).

R.3.4 Начало и окончание программы


Программа должна содержать функцию с именем main(). Ей приписывается
роль начала программы. Эта функция не является предопределенной
для транслятора, она не может быть перегружена, а ее тип зависит
от реализации. Предполагается, что любая реализация должна
допускать два приведенных ниже определения и что можно добавлять
после argv любые параметры. Функция main может определяться так

   int main() { /* ... */ }

или

   int main(int argc, char* argv[]) { /* ... */ }

В последнем определении argc задает число параметров, передаваемых
программе окружением, в котором она выполняется. Если argc не
равно нулю, параметры должны передаваться как строки, завершающиеся
символом '\0', с помощью argv[0] до argv[argc-1], причем
argv[0] должно быть именем, под которым программа была запущена,
или "". Должно гарантироваться, что argv[argc]==0.
    Функция main() не должна вызываться в программе. Связывание
main() ($$R.3.3) зависит от реализации. Нельзя получать адрес
main() и не следует описывать ее как inline или static.
Вызов функции

    void exit(int);

описанной в <stdlib.h>, завершает программу. Значение параметра
передается окружению программы в качестве результата программы.
    Инициализация нелокальных статических объектов ($$R.3.5)
единицы трансляции происходит прежде первого обращения к функции
или объекту, определенному в этой единице трансляции. Эта
инициализация ($$R.8.4, $$R.9.4, &&R.12.1, $$R.12.6.1) может
быть проведена перед выполнением первого оператора main() или
отложена до любого момента, предшествующего первому использованию
функции или объекта, определенных в данной единице трансляции.
Все статические объекты по умолчанию инициализируются нулем ($$R.8.4)
прежде любой динамической (во времени выполнения программы)
инициализации. Больше никаких требований на порядок инициализации
объектов из различных единиц трансляции не налагается. Инициализация
локальных и статических объектов описана в $$R.8.4.
     Деструкторы ($$R.12.4) для инициализированных статических
объектов вызываются при возврате из main() или при вызове exit().
Уничтожение происходит в обратном порядке по сравнению с
инициализацией. С помощью функции atexit() из <stdlib.h> можно
указать функцию, которую нужно вызывать при выходе из программы.
Если было обращение к функции atexit(), объекты, инициализированные
до вызова atexit(), не должны уничтожаться до тех пор, пока не
произойдет вызов функции, указанной в atexit(). Если реализация С++
сосуществует с реализацией С, все действия, которые должны были
произойти после вызова функции, заданной в atexit(), происходят
только после вызова всех деструкторов.
    Вызов функции

    void abort();

описанной в <stdlib.h>, завершает программу без выполнения
деструкторов статических объектов и без вызова функций, заданных
в atexit().

R.3.5 Классы памяти


Существует два описываемых класса памяти: автоматический и статический.
   Автоматические объекты становятся локальными при передаче
управления в каждый блок.
   Статические объекты существуют и сохраняют свое значение во все
время выполнения программы.
   Автоматические объекты инициализируются ($$R.12.1) всякий раз,
когда управление переходит в блок, где они определены и уничтожаются
($$R.12.4) по выходе из этого блока ($$R.6.7).
   Поименованный автоматический объект не должен быть уничтожен
до окончания его блока, точно так же, как не может быть исключен
поименованный автоматический объект класса, имеющего конструктор
или деструктор с побочным эффектом, даже если кажется, что этот
объект не используется.
   Аналогично, глобальный объект класса с конструктором или
деструктором, имеющими побочный эффект, не может быть исключен,
даже если кажется, что он не используется.
  Статические объекты инициализируются и уничтожаются в
соответствии с описанием в $$R.3.4 и $$R.6.7. С некоторыми объектами
не связано никакого имени, см. $$R.5.3.3 и $$R.12.2. Все глобальные
объекты имеют класс памяти статический. Локальным объектам и членам
класса можно предать класс памяти статический с помощью явного
использования спецификации класса памяти static ($$R.7.1.1).

R.3.6 Типы


Существуют два вида типов: основные и производные.

R.3.6.1 Основные типы


Существует несколько основных типов. В стандартном заголовочном
файле <limits.h> задаются в зависимости от реализации минимальные и
максимальные значения каждого типа.
   Объекты, описанные как символы (char), могут хранить любой
элемент из базового набора символов данной машины. Если символ
этого набора хранится в символьной переменной, то ее значение
равно целому значению кода этого символа. Символы могут явно
описываться как unsigned или signed. Обычный char, signed char и
unsigned char являются тремя различными типами. Для всех этих
трех типов требуется одинаковый объем памяти.
   С помощью описаний short int, int и long int можно определить
целые трех различных размеров. Для длинных целых требуется памяти
не меньше чем для коротких целых, но в реализации или короткие
целые, или длинные целые, или и те и другие могут оказаться
эквивалентными обычным целым. Обычные целые имеют размер,
определяемый системой команд, размеры других целых определяются
конкретными потребностями.
   Для каждого из типов signed char, short, int и long существует
соответствующий беззнаковый тип, который занимает тот же объем
памяти и удовлетворяет тем же требованиям выравнивания.
Требование выравнивание - это ограничение на значение указателя
на данный объект, которое накладывает реализация ($$R.5.4).
   Беззнаковые целые, описанные как unsigned, подчиняются законом
арифметики по модулю 2@n, где n число битов, используемое для
представления значения. Отсюда следует, что в арифметике беззнаковых
не возникает переполнения.
  Существует три типа с плавающей точкой: float, double и long double.
Тип double гарантирует не меньшую точность представления, чем
float, а тип long double гарантирует точность не меньше, чем у
double. Характеристики основных типов с плавающей точкой определяются
в соответствии с реализацией в стандартном заголовочном файле
<float.h>.
    Типы char, int любых размеров и тип перечисления ($$R.7.2)
называются целочисленными типами. Целочисленные типы вместе с
типами с плавающей точкой образуют арифметические типы.
   Тип void задает пустое множество значений. Он используется для
обозначения типа функций, которые не возвращают результат. Нельзя
описывать объекты с типом void. Любое выражение можно явно
преобразовать к типу void ($$R.5.4), получившееся выражение можно
использовать только как выражение-оператор ($$R.6.2), как
левый операнд операции запятая ($$R.5.18) или в качестве второго или
третьего операнда в операции ?: ($$R.5.16).

R.3.6.2 Производные типы


Существует потенциально бесконечное число производных типов, которые
строятся из основных типов следующими способами:
    массив объектов данного типа, $$R.8.4;
    функции, имеющие параметры данного типа и возвращающие объекты
    данного типа, $$R.8.2.5;
    указатели на объекты или функции данного типа, $$R.8.2.1;
    ссылки на объекты или функции данного типа, $$R.8.2.2;
    константы, являющиеся значениями данного типа, $$R.7.1.6;
    классы, содержащие совокупность объектов различных типов ($$R.9),
    набор функций для управления этими объектами ($$R.9.3) и
    и список ограничений на доступ к этим объектам и функциям, $$R.11;
    структуры, которые являются классами без стандартных ограничений
    на доступ, $$r.11;
    объединения, которые являются структурами, способными содержать
    в разное время объекты различных типов, $$R.9.5;
    указатели на члены классов, которые задают члены данного типа
    среди всех объектов данного класса, $$R.8.2.3.
В общем случае указанные методы построения объектов могут применяться
рекурсивно, ограничения приведены в $$r.8.2.1, $$R.8.2.4, $$R.8.2.5
и $$R.8.2.2.
  Про указатель на объекты типа T говорят "указатель на на T". Например,
про указатель на объект типа int говорят "указатель на int", а
указатель на объект класса X называется "указатель на X".
  Объекты типа void* (указатель на void), const void* и
volatile void* могут использоваться как указатели на объекты
неизвестного типа. Объект типа void* должен иметь достаточно памяти,
чтобы хранить указатель на любой объект.
  Все фрагменты руководства, говорящие об "указателях", не относятся
к указателям на члены, за исключением указателей на статические
члены.

R.3.6.3 Имена типов


Основные и производные типы можно поименовать с помощью механизма
typedef ($$R.7.1.3), а семейство типов и функций можно задать и
поименовать с помощью механизма шаблона типов ($$R.14).

R.3.7 Адреса


Любой объект - это некоторая область памяти, адрес - выражение,
ссылающееся на объект или функцию. Очевидным примером адреса будет
имя объекта. Существуют операции, порождающие адреса, например,
если E выражение типа указатель, то *E - адресное выражение,
соответствующее объекту, на который указывает E. Термин "адрес"
("lvalue" т.е. left value - левая величина) появляется из оператора
присваивания E1 = E2, где левый операнд E1 должен "адресовать"
изменяемую переменную. При обсуждении всех операций в $$R.5 указывается
применимы ли они к адресным операндам и порождают ли они сами адреса.
Адрес может изменяться, если он не является именем функции,
именем массива или const.

R.4 Стандартные преобразования


Некоторые операции в зависимости от своих операндов могут вызвать
преобразование значения операнда от одного типа к другому. Здесь
описываются преобразования, вызванные самыми обычными операциями,
и объясняется каковы могут быть результаты таких преобразований.
По мере надобности будут даны дополнительные объяснения при
обсуждении каждой операции. Подобные преобразования также происходят
при инициализации ($$R.8.4, $$R.8.4.3, $$R.12.8, $$R.12.1).
В $$R.12.3  и $$R.13.2 описываются преобразования, заданные
пользователем, и их соотношения со стандартными преобразованиями.
В результате преобразования может получиться адрес, только если
результат есть ссылка ($$R.8.2.2).

R.4.1 Стандартные преобразования для целочисленных


Всюду, где требуется целое, можно использовать char, short int,
элемент перечисления ($$R.7.2) или битовое поле ($$R.9.6), причем
в знаковом и беззнаковом вариантах. Если int может представлять
все значения исходного типа, значение преобразуется к int, иначе
оно преобразуется к unsigned int. Это называется стандартным
преобразованием для целочисленных.

R.4.2 Преобразования целочисленных


Если целое преобразуется в беззнаковый тип, то полученное значение
есть наименьшее беззнаковое целое, совпадающее с целым со знаком
по (mod 2**n), где n есть число битов в представлении беззнакового
целого. Для представления в дополнительном коде это преобразование
лишь концептуальное, никаких изменений в двоичном представлении
в действительности не происходит.\
    Если целое преобразуется к знаковому типу, значение не меняется,
при условии, что его можно представить с помощью нового типа,
иначе значение определяется реализацией.

R.4.3 Значения с плавающей точкой и двойной точностью


Для выражений типа float может использоваться арифметика с обычной
точностью. Если значение с плавающей точкой меньшей точности
преобразуется в значение типа float равной или большей точности,
то изменения значения не происходит. Если значение с плавающей
точкой большей точности преобразуется в значение типа float
меньшей точности и значение находится в пределах, задаваемых
представлением типа, то в результате может получиться или
ближайшее большее или ближайшее меньшее представимое значение.
Если результат оказался вне границ представления типа, поведение
неопределено.

R.4.4 Целочисленные и числа с плавающей точкой


Преобразование значения с плавающей точкой к целочисленному типу
сводится к "усечению", т.е. отбрасыванию дробной части. Такие
преобразования зависят от машины, в частности в какую сторону будет
проходить усечение для отрицательных чисел определяется на разных
машинах по разному. Результат считается неопределенным, если
значение нельзя представить в целочисленном типе.
    Преобразования целочисленных значений к значениям с плавающей
точкой математически корректны настолько, насколько это позволяет
система команд. Может произойти потеря точности, если целочисленное
значение нельзя точно представить как значение с плавающей точкой.


  R.4.5 Арифметические преобразования

Для большинства операций преобразования операндов и тип результата
определяются одними и и теми же правилами. Это правило можно назвать
"обычными арифметическими преобразованиями".
   Если один из операндов есть long double, другой операнд
   преобразуется в long double.
   Иначе, если один из операндов есть double, другой операнд
   преобразуется в double.
   Иначе, если один из операндов есть float, другой операнд
   преобразуется в float.
   Иначе, если стандартные целочисленные преобразования ($$R.4.1)
   происходят над обоими операндами.
   Тогда, если один из операндов есть unsigned long, другой
   операнд преобразуется в unsigned long.
   Иначе, если один из операндов есть long int, а другой -
   unsigned int, то при условии, что long int может представлять
   все возможные значения unsigned int, значение unsigned int
   преобразуется в long int, в противном случае оба операнда
   преобразуются в unsigned long int.
   Иначе, если один из операндов есть long, другой операнд
   преобразуется в long.
   Иначе, если один из операндов есть unsigned, другой операнд
   преобразуется в unsigned.
   Иначе, оба операнда должны быть int.

R.4.6 Преобразования указателей


Всюду, где указатели ($$R.8.2.1) присваиваются, инициализируются,
сравниваются или используются иным образом, могут происходить
следующие преобразования:
   Константное выражение ($$R.5.19), которое сводится к нулю,
   преобразуется в указатель, обычно называемый пустым указателем.
   Гарантируется, что значение такого указателя будет отлично от
   любого указателя на объект или функцию.
   Указатель на объект любого типа, не являющегося const или
   volatile, можно преобразовать в void*.
   Указатель на функцию можно преобразовать в void*, при условии,
   что для void* отводится достаточно памяти, чтобы хранить этот
   указатель.
   Указатель на данный класс можно преобразовать в указатель на
   доступный базовый класс данного класса ($$R.10), если такое
   преобразование не содержит двусмысленность ($$R.10.1). Базовый
   класс считается доступным, если доступны его общие члены ($$R.11.1).
   Результатом преобразования будет указатель на объект типа базового
   класса, вложенный в объект типа производного класса. Пустой указатель
   (0) преобразуется сам в себя.
   Выражение типа "массив T" может преобразовываться в указатель
   на начальный элемент массива.
   Выражение типа "функция, возвращающая T" преобразуется в "указатель
   на функцию, возвращающую T", за исключением тех случаев, когда
   оно используется как операнд адресной операции & или операции
   вызова функции ().

R.4.7 Преобразования ссылок


Всюду, где ссылки ($$R.8.2.2) инициализируются (включая передачу
параметров ($$R.5.2.2) и возврат значения функции ($$R.6.6.3)) или
используются иным образом, возможны следующие преобразования:
   Ссылка на данный класс может быть преобразована в ссылку на доступный
   базовый класс ($$R.10, $$R.11.1) данного класса ($$R.8.4.3),
   при условии, что такое преобразование не содержит двусмысленности
   ($$R.10.1.1). Результатом преобразования будет ссылка на объект
   базового класса, вложенный в объект производного класса.

R.4.8 Указатели на члены


Всюду, где указатели на члены ($$R.8.2.3) инициализируются,
присваиваются, сравниваются или используются иным образом,
могут происходить следующие преобразования:
   Константное выражение ($$R.5.19), которое сводится к нулю,
   преобразуется в указатель на член. Гарантируется, что его
   значение будет отлично от любых других указателей на члены.
   Указатель на член данного класса можно преобразовать в
   указатель на член производного от данного класса, при условии,
   что допустимо обратное преобразование от указателя на член производного
   класса в указатель член базового класса, и что оно выполнимо
   однозначным образом ($$R.10.1.1).
   Правило преобразования указателей на члены (т.е. от указателя на
член базового класса к указателю на член производного класса) выглядит
перевернутым, если сравнивать его с правилом для указателей на
объекты (т.е. от указателя на производный объект к указателю на
базовый объект) ($$R.4.6, $$R.10). Это необходимо для гарантии
надежности типов.
   Отметим, что указатель на член не является указателем на объект
или указателем на функцию и правила преобразований таких указателей
не применимы для указателей на члены. В частности указатель на член
нельзя преобразовать в void*.

R.5 Выражения


Здесь определяются синтаксис, порядок вычисления и назначение
выражений. Выражение - это последовательность операций и операндов,
которая задает вычисление. Вычисление может выдавать в качестве
результата значение и может вызывать побочные эффекты.
   Операции могут быть перегружены, т.е. им может быть приписано значение,
когда они применяются к выражениям типа класс ($$R.9). Применение
перегруженных операций преобразуется в вызовы функций в соответствии
с описанием в $$R.13.4. Перегруженные операции подчиняются
синтаксическим правилам, определенным в этом разделе, но требования
к типу операнда, адресу и порядку вычисления заменяются на правила
вызова функции. Соотношения между операциями, типа ++a означает
a+=1, не гарантируются для перегруженных операций ($$R.13.4).
   В этом разделе описано применение операций к типам, для которых
они не являются перегруженными. Перегрузка операций не может изменить
правила применения операций к типам, для которых такое применение
предусмотрено в самом языке.
   Порядок вычисления подвыражений определяется приоритетом и порядком
применения операций. Обычные математические правила ассоциативности
и коммутативности операций действуют только, если операции
действительно ассоциативны или коммутативны. За исключением
оговоренных случаев порядок вычисления операндов конкретной операции
неопределен. В частности, если в выражении значение изменяется
дважды, результат выражения неопределен, если только порядок
выполнения не обеспечивается самими операциями, например:

    i = v[i++];  // the value of `i' is undefined
    i=7,i++,i++; // `i' becomes 9

   Реакция на переполнение и деление на нуль при вычислении выражения
зависит от реализации. В большинстве существующих реализаций С++
игнорируется переполнение целых. Реакция на деление на нуль и
ошибки операций над числами с плавающей точкой варьируется от
машины к машине и обычно связана с соответствующими библиотечными
функциями.
   Кроме оговоренных случаев, операнды типа const T, volatile T,
T&, const T& и volatile T& можно использовать, как если бы они
имели тип просто T. Аналогично, операнды типа T* const, T*volatile
можно использовать, как если бы они имели тип просто T*, за
исключением оговоренных случаев. Аналогично, просто тип T можно
использовать всюду, где требуется тип volatile T или const T.
Эти правила может применять в комбинации, так что const T* volatile
можно использовать там, где требуется T*, за исключением оговоренных
случаев. При рассмотрении разрешения перегрузки ($$R.13.2) такое
использование операций не считается стандартным преобразованием
операндов.
    Если выражение имеет тип "ссылка на T" ($$R.8.2.2, $$R.8.4.3),
значение выражение есть объект типа "T", на который настроена
ссылка. Выражение является адресом. Ссылку можно представлять как
имя объекта.
    Допустимы определенные пользователем преобразования объектов
класса в (и обратно) основные типы, указатели и т.д. ($$R.12.3)
Если они недвусмысленны ($$R.13.2), такие преобразования могут
применяться транслятором всегда, когда появляется объект типа класса
в качестве операнда операции, в качестве инициализирующего
выражения ($$R.8.4), в качестве выражения, задающего условие ($$R.6.4),
или в качестве выражения, используемого в операторе цикла ($$R.6.5),
или в качестве значения, возвращаемого функцией ($$R.6.6.3),
или в качестве параметра функции ($$R.5.2.2).

R.5.1 Первичные выражения


Первичными выражениями являются литералы, имена и имена, определенные
с помощью операции разрешения области видимости ::.

     первичное-выражение:
          литерал
          this
          :: идентификатор
          :: имя-функции-операции
          :: уточненное-имя
          ( выражение )
          имя

   Литерал является первичным выражением. Его тип определяется
его видом ($$R.2.5).
   В теле нестатической функции-члене ($$R.9.3) служебное слово
this обозначает указатель на объект, к которому относится вызов
функции. Служебное слово this нельзя использовать вне тела
функции-члена класса.
   Операция :: , за которой следует идентификатор или
имя-операции-функции или уточненное-имя являются первичным
выражением. Его тип задается описанием идентификатора, имени
или имени-функции-операции. Результатом является идентификатор,
имя или имя-функции-операции. Результат является адресом, если
идентификатор является адресом. Идентификатор или имя-функции-операции
должны иметь файловую область видимости. С помощью операции ::
можно обращаться к типу, объекту, функции или элементу перечисления,
даже если обозначающий их идентификатор является скрытым ($$R.3.2).
   Выражение в скобках является первичным выражением, тип и значение
которого идентичны им же у выражения без скобок. Наличие скобок
не влияет на то, является выражение адресом или нет.
   Понятие имя - это определенное первичное-выражение, которое
может появляться только после . и -> ($$R.5.2.4):

     имя:
        идентификатор
        имя-функции-операции
        имя-функции-преобразования
        ~имя-класса
        уточненное-имя

  Идентификатор есть имя, при условии что он описан надлежащим образом
($$R.7). Понятие имя-функции-операции описано в ($$R.13.4), а
понятие имя-функции-преобразования в ($$R.12.3.2). Конструкция
~имя-класса обозначает деструктор ($$R.12.4).

       уточненное-имя:
                уточняющее-имя-класса :: имя

Понятие уточняющее-имя-класса, за которым следует :: и имя члена
этого класса ($$R.9.2), или члена базового по отношению к данному
класса ($$R.10) является уточненное-имя. Его тип есть
тип члена, а результат выражения есть этот член. Результат является
адресом, если таковым является член. Имя класса может быть скрыто
другим именем (не типа), в таком случае все равно имя класса
доступно и его можно использовать. Если используется
имя-класса::имя-класса или имя-класса::~имя-класса, оба понятия
имя-класса должны обозначать один и тот же класс. С помощью такой
записи обозначаются конструкторы ($$R.12.1) и деструкторы ($$R.12.4)
соответственно. Можно использовать уточняющие имена
неоднократно, например, N1::N2::N3::n, чтобы обозначать вложенные
типы ($$R.9.7).

R.5.2 Постфиксные выражения


   Постфиксные выражения применяются слева направо.
      постфиксное-выражение:
             первичное-выражение
             постфиксное-выражение [ выражение ]
             постфиксное-выражение ( список-выражений opt )
             имя-простого-типа     ( список-выражений opt )
             постфиксное-выражение .  имя
             постфиксное-выражение -> имя
             постфиксное-выражение ++
             постфиксное-выражение --
      список-выражений:
            выражение-присваивания
            список-выражений , выражение-присваивания

R.5.2.1 Индексация


Постфиксное выражение, за которым следует выражение в квадратных
скобках, является постфиксным выражением. Интуитивный смысл его
индексирование. Первое из выражений должно иметь тип "указатель на T",
а второе быть целочисленного типа. Тип результата есть "T". Выражение
E1[E2] совпадает (по определению) с выражением *((E1) + (E2)).
Подробности операций * и + даны в $$R.5.3  и $$R.5.7, а массивы
обсуждаются в $$R.8.2.4.

R.5.2.2 Вызов функции


Вызов функции является постфиксным выражением, за которым следует,
возможно пустой, список выражений в скобках, разделенных запятой.
Эти выражения образуют фактические параметры функции. Постфиксное
выражение должно иметь тип "функция, возвращающая T", "указатель на
функцию, возвращающую T" или "ссылка на функцию, возвращающую T",
а результат операции вызова имеет тип "T".
    При вызове функции происходит инициализация каждого формального
параметра ($$R.8.4.3, $$R.12.8, $$r.12.1) фактическим параметром.
Производятся стандартные ($$R.4) и заданные пользователем ($$R.12.3)
преобразования типа. В функции может изменяться значения непостоянных
формальных параметров, но эти изменения не могут повлиять на значения
фактических параметров, кроме того случая, когда формальный параметр
имеет тип ссылки без спецификации const ($$R.8.2.2). Если формальный
параметр имеет тип ссылки при необходимости может создаваться
временная переменная ($$R.7.1.6, $$R.2.5,$$R.2.5.4,$$R.8.2.4,
$$R.12.2). Добавим, что возможно изменение непостоянных объектов с
помощью параметров-указателей.
   Функцию можно описать таким образом, что она сможет использовать
меньшее число параметров (определив параметры по умолчанию $$R.8.2.6)
или большее число параметров (с помощью эллипсиса ... $$R.8.2.5),
чем было указано при определении функции ($$R.8.3).
   Функцию можно вызвать только, если описание ее доступно в той области
видимости, где происходит вызов. Отсюда следует, всякий формальный
параметр, соответствующий некоторому фактическому параметру, должен
быть доступен, если не считать эллипсис (...).
    Перед вызовом всякий фактический параметр типа float, для которого
нет формального параметра, преобразуется к типу double,  а типа
char, short, перечисления или битовое поле, для которого нет
формального параметра, преобразуется к типу int или unsigned
согласно стандартным преобразованиям целочисленных ($$R.4.1).
Объект, являющийся классом и не имеющий описания формального параметра,
передается при вызове как структура данных.
    Объект, являющийся классом и имеющий описание формального
параметра передается с помощью инициализации формального параметра
фактическим параметром, которая происходит перед выполнением
функции посредством вызова конструктора ($$R.12.2, $$R.12.8).
    Порядок вычислений параметров неопределен и учтите, что он
может быть различен у разных трансляторов. Все побочные эффекты
выражений фактических параметров могут происходить перед началом
выполнения функции. Порядок вычисления постфиксных выражений и
списка выражений параметров неопределен.
    Допустимы рекурсивные вызовы.
    Операция вызова функции порождает адрес только, если тип
результата есть адрес.

R.5.2.3 Явные преобразования типа


Конструкция имя-простого-типа ($$R.7.1.6), за которой следует
список-выражений в скобках образует значение указанного типа
с учетом списка выражений. Если список выражений содержит более
одного значения, тип должен быть классом с конструктором, описанным
соответствующим образом ($$R.8.4, $$R.12.1).
   Конструкция имя-простого-типа ($$R.7.1.6), за которой следует
пара скобок (пустая), образует значение указанного типа. Если тип
является классом с конструктором, описанным соответствующим образом,
будет вызван этот конструктор, в противном случае результатом
будет неопределенное значение указанного типа, см. так же ($$R.5.4).

R.5.2.4 Доступ к члену класса


Постфиксное выражение, за которым следует точка (.) и имя, является
постфиксным выражением. Первое выражение должно быть объектом типа
класс, а имя должно быть именем члена этого класса. Результатом будет
поименованный член объекта и он будет адресом, если член является
адресом.
   Постфиксное выражение, за которым следует стрелка (->)  и имя,
является постфиксным выражением. Первое выражение должно быть
указателем на объект типа класс, а имя должно быть именем члена
этого класса. Результатом будет поименованный член объекта, на
который настроен указатель и он будет адресом, если член является
адресом. Значит выражение E1->MOS тоже самое, что (*E1).MOS.
   Обратите внимание, что "объекты типа класс" могут быть
структурами ($$R.9.2) или объединениями ($$R.9.5). Классы обсуждаются
в $$R.9.

R.5.2.5 Инкремент и декремент


Значение, получаемое в результате применения постфиксной операции ++,
есть значение операнда. Операнд должен быть изменяемым адресом.
Тип операнда должен быть арифметический или тип указателя. После
выборки результата (для дальнейшего использования) объект увеличивается
на 1. Тип результата совпадает с типом операнда, но не является
адресом (см. так же $$R.5.7 и $$R.5.17).
   Постфиксная операция -- сводится к операции декремента (уменьшение
на 1) и аналогична операции ++.

R.5.3 Унарные операции


Выражения с унарными операциями выполняются справа налево.

    унарное-выражение:
         постфиксное-выражение
         ++ унарное выражение
         -- унарное выражение
         унарная-операция выражение-приведения
         sizeof унарная-операция
         sizeof ( имя-типа )
         выражение-размещения
         выражение-освобождения
     унарная-операция: один из
          *  &  +  -  !  ~

Унарная операция * означает косвенность: выражение должно быть
указателем, а результат является адресом, ссылающимся на объект, на
который указывает выражение. Если тип выражения есть "указатель на T",
то тип результата будет "T".
   Результатом унарной операции & будет указатель на ее операнд.
Операнд должен быть функцией или адресом или конструкцией
уточненное-имя. Для первых двух случаев, если тип выражения
есть "T", то тип результата будет "указатель на T". В частности,
адрес объекта типа const T имеет тип const T*, тоже верно для
volatile. Для случая уточненное имя если член класса "C" не является
статическим и имеет тип "T", то тип результата операции будет
"указатель на член C типа T". Для статических членов типа T
результатом будет обычный "указатель на T". Адрес перегруженной
функции ($$R.13) можно брать только при инициализации или
присваивании, в котором левая часть однозначно определяет какая
версия перегруженной функции имеется ввиду ($R13.3).
    Операнд унарной операции + должен быть арифметического типа
или типа указатель и результатом будет значение операнда. Для
целочисленных операндов  производится стандартное преобразование
целочисленных. Тип результата есть тип преобразованного операнда.
    Операнд унарной операции - должен иметь арифметический тип и
результатом будет изменение знака операнда. Для целочисленных
операндов выполняется стандартное преобразование целочисленных.
Операция для беззнаковых величин выполняется с помощью вычитания
значения операнда из 2**n, где n число битов в представлении
преобразованного операнда. Тип результата есть преобразованного
операнда.
    Операнд операции логического отрицания ! должен иметь
арифметический тип или быть указателем, результат равен 1, если
значение операнда есть 0, и равен 0, если операнд не равен 0.
Тип результата есть int.
   Операнд операции ~ должен иметь целочисленный тип, результатом
будет обращение двоичного представления операнда. Выполняются
стандартные преобразования целочисленных. Тип результата есть
тип преобразованного операнда.

R.5.3.1 Инкремент и декремент


Операнд префиксной операции ++ увеличивается на 1. Операнд должен
быть изменяемым адресом. Тип операнда должен быть арифметическим
или указателем. Результатом является новое значение операнда,
оно считается адресом. Выражение ++x эквивалентно x+=1. Для
уточнения преобразований можно обратиться к описанию сложения
($$R.5.7) и операций присваивания ($$R.5.17).
    Префиксная операция -- сводится к уменьшению на 1 и выполняется
аналогично префиксной операции ++.

R.5.3.2 Операция sizeof


Операция sizeof вычисляет размер своего операнда в байтах. Операнд
должен быть или выражением, которое не вычисляется, или именем типа
в скобках. Операцию sizeof нельзя применять к функции, битовому полю,
неопределенному классу, типу void или к массиву с неуказанными
границами индексов. Байт никак не определяется языком, кроме как
результата операции sizeof, именно sizeof(char) есть 1.
   Если операция применяется к ссылке, результатом будет размер
объекта, на который настроена ссылка. Если она применяется к классу,
результатом будет размер объекта этого класса в байтах с учетом
всех дополнительных байтов, которые потребуется для размещения
такого объекта в массиве. Размер любого класса или объекта класса
больше нуля. В случае массива операция выдает полное число байтов
в массиве. Отсюда следует, что размер массива из n элементов равен
размеру элемента, умноженному на n.
   Операция sizeof может применяться к указателю на функцию, но не
к самой функции.
   Результатом операции будет константа типа size_t. Этот тип
определен в стандартном заголовочном файле <stddef.h> и является
зависящим от реализации беззнаковым целочисленным типом.

R.5.3.3 Операция new


Операция new предназначена для создания объекта типа имя-типа
($$R.8.1). Этот тип должен быть типом объекта и функции нельзя
размещать с ее помощью, хотя указатели на функции можно.

     выражение-размещения:
          ::opt new параметры-new opt имя-типа-new инициализатор-new
          ::opt new параметры-new opt ( имя-типа ) инициализатор-new
     параметры-new:
          ( список-выражений )
     имя-типа-new:
          список-спецификаций-типа описатель-new opt
     описатель-new:
          * список-спецификаций-cv opt описатель-new opt
          имя-класса :: список-спецификаций-cv opt описатель-new opt
          описатель-new opt [ выражение ]
     инициализатор-new:
          ( список-инициализаторов opt )

Время жизни объекта, созданного с помощью new, не ограничивается
областью видимости, в которой он был создан. Операция new возвращает
указатель на созданный объект. Если объект является массивом,
возвращается указатель на начальный элемент массива. Например,
обе операции new int и new int[1] возвратят int* , а типом
new int[i][10] будет int(*)[10]. Если описывается тип массива
($$R.8.2.4), все размерности, кроме первой, должны быть выражениями-
константами ($$R.5.19) с положительным значением. Первая размерность
массива может задаваться произвольным выражением, даже если
используется имя-типа (здесь нарушается общее требование, чтобы
размерности массива в конструкции имя-типа были
выражениями-константами ($$R.5.19)).
   Допускается, чтобы вызывалась функция operator new() с параметром
нуль. В таком случае возвращается указатель на объект. При повторении
таких вызовов будут возвращаться указатели на разные объекты.
  Конструкция список-спецификаций-типа не должна содержать const,
volatile, описание класса или перечисления.
  Для резервирования памяти операция new обращается к функции
operator new() ($$R.12.5). При размещении объекта типа T ей в
качестве первого параметра передается sizeof(T). Конструкция
параметры-new используется для передачи дополнительных параметров.
Например, операция new T приводит к вызову operator new(sizeof(T)),
а операция new(2,f) T  приводит к вызову operator new(sizeof(T),2,f).
     Конструкция параметры-new может использоваться только, если
описана функция operator new() с параметрами соответствующих типов.
     Если с помощью операции new создается объект не типа класс
(в том числе и массив объектов типа класс), то вызывается глобальная
функция ::operator new(). Если с помощью new создается объект класса
T, вызывается функция T::operator new(), если она существует
(используя обычные правила просмотра при поиске членов класса и его
базовых классов, $$R.10.1.1), иначе вызывается глобальная функция
::operator new(). Использование операции ::new() гарантирует, что
будет вызываться глобальная функция ::operator new(), даже если
существует T::operator new().
    Конструкция выражение-размещения может содержать инициализатор-new.
Для объектов классов с конструкторами ($$R.12.1) задаваемый ею
список параметров будет использоваться при вызове конструктора, в
других случаях конструкция инициализатор-new должна иметь вид
( выражение ) или ( ). Если выражение присутствует, оно используется
для инициализации объекта, если его нет, объект начнет существование
с неопределенным значением.\
    Если класс имеет конструктор, объект этого класса можно создать
с помощью new только при условии, что заданы подходящие параметры,
или, что класс имеет стандартный конструктор ($$R.12.1).
Отводит ли память при создании объекта типа класс сама функция
operator new, или оставляет это на конструктор, зависит от реализации.
Как для конструктора, так и для функции operator new() проводится
проверка возможности доступа и однозначности ($$R.12).
   Для массивов нельзя задавать инициализаторы. Массивы объектов
типа класса с конструктором можно создавать с помощью операции new
только, если конструктор класса является стандартным ($$R.12.1).
В этом случае стандартный конструктор будет вызываться для каждого
элемента массива.
   Инициализация производится только в том случае, когда функция
operator new() возвращает ненуль. Если она возвращает 0 (пустой
указатель), значение выражения есть 0.
   Порядок вычисления выражения вызова operator new() для получения
памяти и порядок вычисления параметров конструктора неопределен.
Так же неопределено вычисляются ли параметры конструктора, если
функция operator new() возвратила 0.
   В конструкции имя-типа-new скобки использовать необязательно.
Тогда обращение

    new int (*[10])();   // error

может привести к ошибке, т.к. операции применяются в таком порядке

    (new int) (*[10])(); // error

Объекты сложного типа можно задать в операции new с помощью явно
указанных скобок, например, обращение

    new (int (*[10])());

размещает массив из 10 указателей на функции (не имеющие параметров
и возвращающие int).
    Конструкции имя-типа-new в выражение-размещения должна быть
самой длинной из возможных последовательностей конструкций
описатель-new. Это предотвращает коллизии между операциями из
описателей &, *, [] и их двойниками из выражения, например,

     new int* i;   // syntax error: parsed as `(new int*) i'
                   //               not s `(new int)*i'

Символ * используется в описателе указателя, а не в качестве
операции умножения.

R.5.3.4 Операция delete


Операция delete уничтожает объект, созданный с помощью new.

       выражение-освобождения:
          ::opt delete выражение-приведения
          ::opt delete [] выражение-приведения

Результат имеет тип void. Операндом delete должен быть указатель,
который возвращает new. Эффект применения операции delete к указателю,
который не получен в результате операции new без задания
параметры-new, считается неопределенным и обычно приводит к опасным
последствиям. Однако гарантируется, что удаление по указателю с
нулевым значением безопасно.
    Результат попытки доступа к удаленному объекту неопределен, а
удаление объекта может изменить его значение. Более того, если
выражение, задающее объект, является изменяемым адресом, его
значение после удаления неопределено.
    Нельзя удалять указатель на константу.
    Операция delete вызывает деструктор (если он есть $$12.4)
для объекта, на который настроен ее операнд.
    Для освобождения памяти, отведенной под указываемый объект,
операция delete вызывает функцию operator delete ($$R.12.5).
Для объектов, не имеющих тип класс (в том числе и для массивов
классов), используется глобальная функция ::operator delete().
Для объекта типа класс T вызывается функция T::operator delete(),
если она есть (используя обычные правила просмотра при поиске
членов класса и производных от него классов, $$R.10.1.1), в
противном случае вызывается глобальная функция ::operator delete().
Обращение ::delete гарантирует, что будет вызываться глобальная
функция ::operator delete(), даже если существует T::operator delete().
Для удаления массивов используется обращение вида

      delete [ ] выражение-приведения

Здесь выражение должно указывать на массив. Если есть деструкторы,
они будут вызываться для удаления указанных объектов.
    Результат удаления массива с помощью простого обращения delete
неопределен, так же как и удаление одиночного объекта с помощью
delete [].

R.5.4 Явное преобразование типа


Явное преобразование типа можно задать с помощью функциональной
записи ($$R.5.2.3) или с помощью операции приведения.

       выражение-приведения:
           унарное-выражение
           ( имя-типа ) выражение-приведения

Задание с помощью операции приведения используется для обозначения
преобразования к типу, который не является конструкцией
имя-простого-типа.
    В операции приведения нельзя определять типы.
    Всякое преобразование типа, не упомянутое здесь и не являющееся
преобразованием явно определенным пользователем ($$R.12.3), считается
ошибкой.
    Любой тип, который можно преобразовать в другой с помощью
стандартного преобразования ($$R.4), можно также преобразовать
с помощью явного преобразования (приведения) и смысл преобразования
будет тот же.
    Указатель можно преобразовать к любому целочисленному типу,
достаточно большому, чтобы вместить значение указателя. Алгоритм
преобразования зависит от реализации, но предполагается, что он
будет естественным для того, кто знает систему адресации, используемой
машины.
    Значение целочисленного типа может быть явно преобразовано в
указатель. Указатель, преобразованный в целое достаточного размера
(если такие есть в реализации), и преобразованный обратно к типу
указателя, должен иметь свое первоначальное значение. Все другие
детали перевода указателя в целое и обратно зависят от реализации.
    Указатель на объект одного типа может быть преобразован в
указатель на объект другого типа (с соблюдением ограничений, указанных
здесь). Использование получившегося указателя может вызвать
особую адресную ситуацию ("неверный адрес"), если преобразуемый
указатель не
настроен на объект, правильным образом выравненный в памяти.
Гарантируется, что указатель на объект данного размера можно
преобразовать в указатель на объект равного или меньшего размера
и провести обратное преобразование без изменения значения указателя.
На различных машинах двоичное представление указателей может быть
различно как и требования на выравнивания объектов. Составные
объекты выравниваются по самой строгой границе, требуемой их
составляющими. Указатель типа void* считается совместимым с
указателем на объект любого типа.
    Указатель на класс B можно преобразовать в указатель на класс D,
для которого класс B является прямо или опосредованно базовым
классом, если существует однозначное преобразование из D в B
($$R.4.6, $$.R10.1.1) и если B является виртуальным базовым классом
($$R.10.1). Такое приведение от базового класса к производному
классу предполагает, что объект базового класса является вложенным
по отношению к объекту производного класса. В результате получится
указатель, настроенный на объемлющий объект производного класса.
Если объект базового класса не содержится ни в каком объекте
производного класса, такая операция приведения может вызвать
особую ситуацию.
    Пустой указатель (0) преобразуется сам в себя.
    Пока еще неопределенный класс можно использовать в операции
приведения указателя, в этом случае никаких допущений о структуре
класса не делается ($$R.10.1).
    Любой объект можно явно преобразовать к типу ссылки X&, если
указатель на этот объект можно явно преобразовать в тип X*.
В результате приведения к ссылке не происходит вызовов конструкторов
или функций преобразований. Преобразование ссылки на базовый класс
в ссылку на производный класс рассматривается аналогично
преобразованию указателя на базовый класс в указатель на
производный класс, учитывая вопросы однозначности, виртуальных
классов и т.д.
    Результатом приведения к ссылке является адрес, в отличие от всех
остальных приведений. Результат приведения указателя или ссылки
настроен на тот же объект, что и исходное выражение без операции
приведения.
   Указатель на функцию можно явно преобразовать в указатель на
некоторый объект при условии, что тип указателя на этот объект
достаточно велик, чтобы хранить указатель на функцию. Указатель
на некоторый объект можно явно преобразовать в указатель на функцию
при условии, что тип указателя на функцию достаточно велик, чтобы
хранить указатель на этот объект. В обоих случаях, использование
указателя, получившегося в результате преобразования, может
вызвать особую адресную ситуацию, или что-нибудь похуже,
если исходный указатель не настроен на соответствующий объект.
   Указатель на функцию одного типа можно явно преобразовать в
указатель на функцию другого типа. Результат вызова функции с
помощью указателя на функцию, тип которой отличен от типа,
использованного при определении первой функции, неопределен
(см. так же $$R.4.6).
   Объект или значение можно преобразовать в объект типа класс
только при условии, что определен подходящий конструктор или
операция преобразования ($$R.12.3).
  Указатель на член можно явно преобразовать в указатель на другой
член, если оба участвующих типа являются типами указателей
на члены одного класса, или, если оба типа являются указателями
на функцию-член классов, один из которых получается как однозначное
производное от другого ($$R.4.8).
    Указатель на объект с типом, имеющим спецификацию const, можно
привести к указателю с типом без спецификации const. Получившийся
в результате указатель будет настроен на исходный объект.
Объект с типом, имеющим спецификацию const, или ссылку на объект
такого типа можно привести в ссылку на объект с типом без const.
Получившаяся в результате ссылка будет настроена на исходный
объект. В результате попытки изменить этот объект с помощью
такой ссылки или указателя может возникнуть особая ситуация или
он будет таким же, как при обращении с помощью исходной ссылки
или указателя к объекту, тип которого не содержит const. Возникнет
ли особая адресная ситуация зависит от реализации.
   Указатель на объект типа со спецификацией volatile можно привести
к указателю на объект типа без volatile. В результате получится
указатель, настроенный на исходный объект. Объект типа с volatile
или ссылку на такой объект можно привести к ссылке на объект с типом
без volatile.

R.5.5 Операции указатель-на-член


Операции указатель-на-член применяются слева направо.

     выражение-pm:
        выражение-приведения
        выражение-pm .*  выражение-приведения
        выражение-pm ->* выражение-приведения

    Бинарная операция .* связывает свой второй операнд, который должен
иметь тип "указатель на член класса T", с первым операндом, имеющим
тип класс T или такой класс, для которого T является однозначно
определенным и достижимым базовым классом. Результатом будет объект
или функция с типом, задаваемым вторым операндом.
   Бинарная операция ->* связывает свой второй операнд, который должен
иметь тип "указатель на член класса T", с первым операндом, имеющим
тип "указатель на T" или тип "указатель на класс, для которого T
является однозначно определенным и достижимым базовым классом".
Результатом будет объект или функция с типом, задаваемым вторым
операндом.
 Если результат .* или ->* есть функция, то его можно использовать
только в качестве операнда операции вызова функции (), например,
операция

     (ptr_to_obj->*ptr_to_mfct)(10);

приводит к вызову функции-члена, обозначенной ptr_to_mfct, для
объекта, на который настроен указатель ptr_to_obj. Результат
операции .* или ->* является адресом, если второй операнд есть
адрес.

R.5.6 Мультипликативные операции


Мультипликативные операции *, /, и % выполняются слева направо.

    Мультипликативное-выражение:
           выражение-pm
           мультипликативное-выражение * выражение-pm
           мультипликативное-выражение / выражение-pm
           мультипликативное-выражение % выражение-pm

Операнды операций * и / должны иметь арифметический тип, операнды
для % должны быть целочисленного типа. Обычные арифметические
преобразования ($$R.4.5) производятся над операндами и определяют
тип результата.
    Бинарная операция * обозначает умножение.
    Бинарная операция / вычисляет частное, а бинарная операция %
вычисляет остаток от деления первого выражения на второе. Если
второй операнд у / или % есть 0, результат неопределен, иначе
(a/b)*b + a%b должно равняться a. Если оба операнда неотрицательны,
то таким же будет и результат, в противном случае знак результата
определяется реализацией.


 R.5.7 Аддитивные операции

Аддитивные операции + и - выполняются слева направо, при этом
происходят обычные арифметические преобразования ($$R.4.5)
операндов арифметического типа.

       аддитивное-выражение:
             мультипликативное-выражение
             аддитивное выражение + мультипликативное-выражение
             аддитивное-выражение - мультипликативное-выражение

Операнды должны быть арифметического типа или типа указателя.
Результатом операции + является сумма операндов. Можно складывать
указатель на объект в массиве и значение любого целочисленного типа.
Результатом будет указатель того же типа, что и исходный указатель,
но он будет настроен на другой объект массива
с заданным смещением от исходного объекта. Так, если P
есть указатель на объект массива, выражение P+1 является указателем
на следующий объект массива. Если же получившийся в результате
сложения указатель вышел за границы  массива, результат будет
неопределенным, кроме случая, когда указатель настроен на первый адрес
больший верхней границы массива.
    Результатом операции - будет разность операндов. Значение
любого целочисленного типа можно вычитать из указателя, при этом
применяются те же преобразования, что и для операции +.
    Никакие другие сочетания типов для указателей не допустимы.
    Если вычитаются два указателя на объекты одного типа, результатом
будет целочисленное значение со знаком, которое показывает на сколько
объектов этого типа отстоят друг от друга указуемые объекты. Указатели
на соседние элементы массива отстоят на 1. Тип результата зависит от
реализации, но он должен быть определен как ptrdiff_t в стандартном
заголовочном файле <stddef.h>. Результат не определен, если указатели
не настроены на элементы одного массива. Если P есть указатель
на последний элемент массива, то (P+1) - 1 есть P.

R.5.8 Операции сдвига


Операции сдвигов << и >> выполняются слева направо.

      сдвиговое-выражение:
          аддитивное-выражение
          сдвиговое-выражение << аддитивное выражение
          сдвиговое-выражение >> аддитивное выражение

Операнды должны быть целочисленного типа, и над ними производятся
стандартные целочисленные преобразования. Тип результата совпадает
с типом преобразованного левого операнда. Результат не определен,
если правый операнд отрицателен или больше или равен числу разрядов
в двоичном представлении преобразованного левого операнда.
Значением выражения E1<<E2 будет E1 (рассматриваемое как набор
разрядов), сдвинутое влево на E2 разрядов, причем освободившиеся
разряды заполняются нулями. Значением выражения E1>>E2 будет E1,
сдвинутое вправо на E2 разрядов. Если E1 беззнакового типа или
имеет неотрицательное значение, гарантируется, что сдвиг вправо
- логический (заполнение нулями), иначе результат зависит от реализации.

R.5.9 Операции отношения


Операции отношения выполняются слева направо, но этот факт мало что
дает, ибо выражение a<b<c означает (a<b)<c, а вовсе не (a<b)&&(b<c).

       выражение-отношения:
          сдвиговое-выражение
          выражение-отношения <  сдвиговое-выражение
          выражение-отношения >  сдвиговое-выражение
          выражение-отношения <= сдвиговое-выражение
          выражение-отношения >= сдвиговое-выражение

Операнды должны быть арифметического типа или типа указателей.
Операции < (меньше чем), > (больше чем), <= (меньше или равно) и
>= (больше или равно) дают результат 0, если указанное отношение
не выполняется, и 1, если оно выполняется. Тип результата int.
   Над арифметическими операндами выполняются обычные арифметические
преобразования. Над указателями выполняются обычные преобразования
указателей. Предполагается, что любой указатель можно сравнить
с выражением, имеющим результат 0, и любой указатель можно сравнить
с указателем, имеющим тип void* (в этом случае указатель сначала
преобразуется к типу void*). Указатели на объекты или функции
одного типа (после преобразования указателей) можно сравнивать,
результат зависит от взаимного расположения в памяти объектов или
функций.
    Два указателя на один и тот же объект считаются равными. Если
два указателя настроены на нестатические члены одного объекта, то
указатель, настроенный на член, описанный позднее, считается
большим, при условии, что члены не имеют разных спецификаций
указатель-доступа ($$R.11.1), а класс не является объединением.
Если два указателя настроены на нестатические члены одного объекта
и спецификации указателей-доступа ($$R.11.1) этих членов различны,
результат будет не определен. Если два указателя настроены на члены
(данные) одного и того же объединения, они считаются равными. Если два
указателя настроены на элементы одного массива или смотрят за границу
массива, то указатель, настроенный на элемент с большим индексом,
будет большим. Все другие сравнения указателей определяются
реализацией.

R.5.10 Операции сравнения на равенство


     выражение-равенства:
           выражение-отношения
           выражение-равенства == выражение-отношения
           выражение-равенства != выражение-отношения

Операции == (равно) и != (не равно) аналогичны операциям
отношения, за исключением того, что их приоритет ниже. (Таким образом,
операция a<b == c<d дает результат 1, если выражения a<b и c<d
имеют одно и то же значение.)
    Кроме этого, можно сравнивать указатели на члены одного типа.
Производятся преобразования указателя на член ($$R.4.8). Указатель
на член можно сравнить с выражением-константой, которое дает
результат 0.

R.5.11 Поразрядная операция И


     выражение-И:
          выражение-равенства
          выражение-И & выражение-равенства

Выполняются обычные арифметические преобразования, результат -
поразрядная функция И от операндов.  Операция применима только к
целочисленным операндам.

R.5.12 Поразрядная (исключающая) операция ИЛИ


          выражение-исключающего-ИЛИ:
               выражение-И
               выражение-исключающего-ИЛИ ^ выражение-И

Выполняются обычные арифметические преобразования, результат -
поразрядная исключающая функция ИЛИ от операндов. Операция применима
только к целочисленным операндам.

R.5.13 Поразрядная (включающая) операция ИЛИ


      выражение-ИЛИ:
           выражение-исключающего-ИЛИ
           выражение-ИЛИ | выражение-исключающего-ИЛИ

Выполняются обычные арифметические преобразования, результат -
поразрядная функция ИЛИ от операндов. Операция применима только
к целочисленным типам.

R.5.14 Логическая операция И


        логическое-выражение-И:
             выражение-ИЛИ
             логическое-выражение-И && выражение-ИЛИ

Операции && выполняются слева направо. Такая операция дает результат
1, если оба операнда ее отличны от нуля, иначе результат - 0. В
отличие от & при операции && гарантируется вычисление слева направо,
более того, второй операнд не вычисляется, если первый операнд равен 0.
    Операнды не обязательно имеют одинаковый тип, но каждый должен быть
арифметического типа или типа указателя. Тип результата int. Все
побочные эффекты вычисления первого выражения могут возникать до
вычисления второго выражения.

R.5.15 Логическая операция ИЛИ


        логическое-выражение-ИЛИ:
             логическое-выражение-И
             логическое-выражение-ИЛИ || логическое-выражение-И

Операции || выполняются слева направо. Результат операции 1, если
один из ее операндов отличен от нуля, иначе результат - 0. В отличие
от | при операции || гарантируется вычисление слева направо, более
того, второй операнд не вычисляется, если значение первого операнда
отлично от нуля.
   Операнды не обязательно имеют одинаковый тип, но каждый должен быть
арифметического типа или типа указателя. Тип результата int. Все
побочные эффекты вычисления первого выражения могут возникать до
вычисления второго выражения.

R.5.16 Операция условия


        выражение-условия:
             логическое-выражение-ИЛИ
             логическое-выражение-ИЛИ ? выражение : выражение-условия

Условные выражения выполняются слева направо. Первое выражение должно
быть арифметического типа или типа указателя. Оно вычисляется, и,
если результат его отличен от нуля, то результатом условного выражения
будет значение второго выражения, иначе результат - значение третьего
выражения. Все побочные эффекты вычисления первого выражения могут
возникать до вычисления второго или третьего выражения.
      Если второе и третье выражение арифметического типа, и типы их
совпадают, то таким же будет и тип результата, если они различаются, то
выполняются обычные арифметические преобразования, чтобы привести их
к общему типу. Если второе и третье выражение являются
указателями или выражением-константой, дающим результат 0, выполняются
преобразования указателей, чтобы привести результаты выражений к
общему типу. Если второе и третье выражение являются ссылками,
выполняется преобразование ссылок, чтобы привести их к общему типу.
Если второе и третье выражение имеют тип void, общий тип
будет void. Если второе и третье выражение имеют один тип
класс T, общим типом будет T. Иначе, выражение считается недопустимым.
Тип результата есть общий тип. Вычисляется только второе или третье
выражение (но не оба). Результат будет адресом, если второй и
третий операнд одного типа и являются адресами.

R.5.17 Операции присваивания


Существует несколько операций присваивания, все они выполняются
справа налево. Для всех них требуется, чтобы левым операндом был
изменяемый адрес. Тип выражения присваивания совпадает с типом
левого операнда. Результат операции присваивание - значение,
хранящееся в левом операнде после того как произошло присваивание.
Результат является адресом.

      выражение-присваивания:
         выражение-условия
         унарное-выражение операция-присваивания выражение-присваивания
         операция-присваивания: один из
              =  *=  /=  %=  +=  -=  >>=  <<=  &=  ^=  |=

При простом присваивании (=) значение выражения заменяет собой значение
объекта, с которым сопоставляется левый операнд. Если оба операнда
арифметического типа, правый операнд, прежде чем произойдет
присваивание, преобразуется к типу левого операнда. Неявные
преобразования к типу перечисления ($$R.7.2) не производятся, поэтому
если левый операнд имеет тип перечисления, правый операнд должен
быть таким же. Если левый операнд имеет тип указателя, правый
операнд должен быть типа указателя или выражением-константой, дающим
результат 0. Правый операнд преобразуется к типу левого операнда,
прежде выполнения присваивания.
   Указатель типа T* const можно присваивать указателю типа T*, но
обратное присваивание считается незаконным ($$R.7.1.6). Объекты
типа const T или volatile T можно присваивать по адресу типа T или
volatile T (см. так же $$R.8.4).
   Если левый операнд имеет тип указателя на член, правый операнд
должен быть типа указатель на член или выражением-константой,
дающим результат 0; перед присваиванием правый операнд преобразуется
к типу левого операнда.
   Присваивание объектам класса X ($$R.9) задается функцией
X::operator=() ($$R.13.4.3). Если пользователь не определил
свою функцию X::operator=(), для присваивания используется
стандартный вариант ($$R.12.8).  Отсюда следует, что объект класса,
который является прямым или непрямым производным от X,  и
однозначно описан как производный в части public ($$R.4.6),
можно присвоить объекту X.
    Указатель на член класса B можно присваивать указателю на член
того же типа класса D при условии, что D является прямым или
непрямым производным класса B, и однозначно описан как
производный в части public ($$R.10.1.1).
    Присваивание объекту типа "ссылка на T" сводится к присваиванию
объекту типа T, который обозначается ссылкой.
   Выполнение выражение вида E1 op= E2 эквивалентно выполнению
E1 = E1 op (E2), однако E1 вычисляется лишь один раз. В операциях
+= и -= левый операнд может быть указателем, в этом случае правый
(целочисленный) операнд преобразуется так, как объяснялось в $$R.5.7.
Все правые операнды и все левые операнды, не являющиеся ссылками,
должны быть арифметического типа.
   Для объектов класса присваивание в общем случае не совпадает с
инициализацией ($$R.8.4, $$R.12.1, $$R.12.6, $$R.12.8).

R.5.18 Операция запятая


Операции запятая выполняются слева направо.

       выражение:
            выражение-присваивания
            выражение, выражение-присваивания

Пара выражений, разделенных запятой, вычисляется слева направо и
значение левого выражения уничтожается. Все побочные эффекты вычисления
левого выражения могут возникать до вычисления правого выражения.
Тип и значение результата совпадают с типом и значением правого
выражения. Результат является адресом, если таковым является
правое выражение.
    В контекстах, где запятая имеет специальное значение, скажем
в списке фактических параметров функции ($$R.5.2.2) или в списке
инициализаторов ($$R.8.4), описанная здесь операция запятая
может появляться только в скобках, например, вызов функции

     f(a, (t=3,t+2), c);

содержит три параметра, причем второй имеет значение 5.

R.5.19 Выражения-константы


В нескольких местах описания С++ требуются выражения, которые
дают в результате целочисленную константу, например: в задании границ
массива ($$R.8.2.4), в выражениях case ($$R.6.4.2),
для задания длины битового поля ($$R.9.6) и как инициализирующее
значение элемента перечисления ($$R.7.2).

       выражение-константа:
             выражение-условия

В конструкции выражение-константа могут участвовать: литералы
($$R.2.5), элементы перечисления, значения целочисленного типа со
спецификацией const, инициализированные выражением-константой
($$R.8.4) и выражения sizeof. Константы с плавающей точкой ($$R.2.5.3)
должны быть приведены к целочисленному типу. Допустимы только
преобразования типа к целочисленному типу. В частности не допустимы
функции, объекты классов, указатели и ссылки, если не считать их
использования в sizeof. Операция запятая и операция присваивания
не допустимы в выражении-константе.

R.6 Операторы


Все операторы, за исключением оговоренных случаев, выполняются
один за другим.

     оператор:
       помеченный-оператор
       оператор-выражение
       составной-оператор
       выбирающий-оператор
       оператор-цикла
       оператор-перехода
       оператор-описания

R.6.1 Помеченный оператор


Оператор можно снабдить меткой.

       помеченный-оператор:
           идентификатор : оператор
           case выражение-константа : оператор
           default : оператор

Использование идентификатора в качестве метки является ее определением.
Идентификатор метки может использоваться помимо этого только в качестве
указания перехода в операторе goto. Областью видимости метки является
функция, в которой она появилась. Метки нельзя повторно описывать
в пределах одной функции. Нельзя использовать метку в операторе goto
до ее определения. Метки имеют свое пространство именования и
они не вступают в коллизию с другими идентификаторами.
    Метки в case или default могут встречаться только в операторе
переключателя.

R.6.2 Оператор-выражение


Чаще всего операторами бывают выражения; в этом случае оператор
имеет такой вид:

        оператор-выражение:
            выражение opt ;

Обычно операторы-выражения являются присваиваниями или вызовами
функций. Все побочные эффекты выполнения оператора-выражения
происходят до выполнения следующего оператора. Оператор-выражение с
отсутствующим выражением называется пустым оператором. Он
может пригодиться, если необходимо поставить метку перед самым концом
составного оператора ({) или для задания пустого тела оператора
цикла while ($$R.6.5.1).

R.6.3 Составной оператор или блок


Для тех случаев, когда вместо одного оператора нужно использовать
несколько, предусмотрен составной оператор (иногда его называют
"блок").

           составной-оператор:
               { список-операторов opt }

           список-операторов:
                оператор
                список-операторов оператор

Отметим, что описание считается оператором ($$R.6.7).

R.6.4 Выбирающий оператор


Выбирающие операторы выбирают одну из нескольких структур управления.

        выбирающий-оператор:
             if ( выражение ) оператор
             if ( выражение ) оператор else оператор
             switch ( выражение ) оператор

Оператор в выбирающем-операторе не может быть описанием.

R.6.4.1 Оператор if


Выражение должно быть арифметического типа, или типа указателя, или
типа класс, для которого существует однозначное преобразование
в арифметический тип или тип указателя ($$R.12.3).
    Вычисляется выражение, и если оно имеет отличный от нуля результат,
выполняется первый вложенный оператор. Если использована конструкция
else и выражение дает результат 0, выполняется второй вложенный
оператор. Неоднозначность в случае нескольких конструкциями else
разрешается путем отнесения else к последнему встретившемуся if,
для которого не было else.

R.6.4.2 Оператор переключателя


Оператор переключателя вызывает передачу управления на один из
нескольких операторов в зависимости от значения выражения.
    Выражение должно быть целочисленного типа или типа класса, для
которого существует однозначное преобразование к целочисленному
типу ($$R.12.3). Выполняются стандартные целочисленные преобразования.
Любой из операторов переключателя можно пометить одним или несколькими
префиксами, имеющими вид:

     case выражение-константа :

Здесь выражение-константа ($$R.5.19) приводится к преобразованному
типу выражения переключателя. Никакие две константы из case одного
переключателя не должны иметь одинаковое значение.
    В переключателе может быть только один префикс вида

     default:

   Операторы переключателя могут быть вложенными, тогда метки из
case или default относятся к самому первому переключателю,
объемлющему их.
   При выполнении оператора переключателя вычисляется выражение,
 и его значение сравнивается с каждой из констант вариантов (case).
Если одна из этих констант равна значению выражения, то управление
передается в оператор, идущий за этой константой. Если ни одна из
констант не совпала со значением выражения, но есть префикс
default, то управление передается на оператор с этим префиксом.
Если префикса default нет, и совпадения не было, то не выполняется
ни один из операторов переключателя.
   Если операторы, выполняемые в результате выбора, не приводят
к каким-либо передачам управления, то программа продолжает выполняться
"по меткам case и default" беспрепятственно. Выход из переключателя
возможен с помощью оператора break (см. $$R.6.6.1).
   Обычно оператор, с которым имеет дело переключатель, бывает
составным. Описания могут появиться в операторах переключателя.
Однако переход ниже описания, в котором была явная или неявная
инициализация, считается незаконным, если только описание не
находится во внутреннем блоке, который обходится (т.е. полностью
обходится при передаче управления, $$R.6.7). Отсюда следует,
что описание с явной или неявной инициализацией должно содержаться
во внутреннем блоке.

R.6.5 Операторы цикла


Эти операторы задают виды цикла.

    оператор-цикла:
          while ( выражение ) оператор
          do оператор  while (выражение)
          for ( оператор-иниц выражение opt ; выражение opt ) оператор
          оператор-иниц:
                  оператор-выражение
                  оператор-описание

  Обратите внимание, что конструкция оператор-иниц кончается точкой с
запятой.
  Оператор в операторе-цикла не должен быть описанием.

R.6.5.1 Оператор while


В операторе while вложенный оператор выполняется до тех пор,
пока значение выражения не станет равным нулю. Проверка происходит
перед каждым выполнением оператора.
  Выражение должно быть арифметического типа, или типа указателя, или
типа класс, для которого существует однозначное преобразование в
арифметический тип или тип указателя ($$R.12.3).

R.6.5.2 Оператор do


В операторе do вложенный оператор выполняется до тех пор,
пока значение выражения не станет равным нулю. Проверка происходит
после каждого выполнения оператора.
    Выражение должно быть арифметического типа, или типа указателя,
или типа класс, для которого существует однозначное преобразование
в арифметический тип или тип указателя ($$R.12.3).

R.6.5.3 Оператор for


Оператор for
   for (оператор-иниц выражение-1 opt ; выражение-2 opt ) оператор
эквивалентен конструкции
   оператор-иниц
   while (выражение-1) {
         оператор
         выражение-2 ;
   }

за исключением того факта, что оператор continue в операторе for
вызовет выполнение выражение-2 перед тем& как начать повторное
вычисление выражения-1. Таким образом, первый оператор задает
инициализацию для цикла, первое выражение производит проверку,
выполняемую перед каждым шагом цикла, так что цикл завершается, когда
выражение становится нулем, а второе выражение обычно задает
приращение, и оно добавляется после каждого шага цикла. Первое
выражение должно иметь арифметический тип, или тип указателя, или
тип класса, для которого существует однозначное преобразование
к арифметическому типу или типу указателя ($$R.12.3).
    Могут быть опущены одно или оба выражения. Если отсутствует
выражение-1, то эквивалентный цикл с while имеет условие while (1).
    Если оператор-иниц является описанием, область видимости имен,
описанных в нем, простирается до конца блока, закрывающего оператор
for.

R.6.6 Операторы перехода


Операторы перехода делают безусловную передачу управления.

     оператор-перехода:
           break ;
           continue ;
           return выражение opt ;
           goto идентификатор ;

  По выходе из области видимости (каким бы образом это не произошло)
вызываются деструкторы ($$R.12.4) для всех объектов классов,
построенных в этой области, которые еще не были уничтожены. Это
относится как к явно описанным объектам, так и ко временным объектам
($$R.12.2).

R.6.6.1 Оператор break


Оператор break может встретиться только в операторе цикла или
переключателе, он приводит к окончанию ближайшего из объемлющих
его операторов цикла или переключателей. Управление передается на
оператор, следующий непосредственно за заканчиваемым, если такой есть.

R.6.6.2 Оператор continue


Оператор continue может встретиться только в операторе цикла и
приводит к передаче управления в заголовок ближайшего
из объемлющих операторов цикла, т.е. в конец цикла. Более точно
можно сказать, что в каждом из операторов:

    while (foo) {      do  {           for (;;) {
    // ...             // ...          // ...
    contin: ;          contin: ;       contin: ;
    }                  } while (foo);  }

оператор continue, не относящийся ко внешним операторам цикла,
эквивалентен оператору goto contin.

R.6.6.3 Оператор return


Возврат из функции в обратившуюся к ней функцию происходит с помощью
оператора return.
   Оператор return без выражения можно использовать только в
функциях, которые не возвращают значение, т.е. в функциях,
возвращающих значение типа void, или в конструкторах ($$R.12.1)
и деструкторах ($$R.12.4). Оператор return с выражением можно
использовать только в функциях, которые возвращают значение. Значение
выражения передается в ту функцию,которая вызвала данную функцию. Если
нужно, значение преобразуется к типу функции, в которой выполняется
return, по тем же правилам как при инициализации. Это может привести
к вызову конструктора или копированию временных объектов ($$R.12.2).
Выход из функции по концу эквивалентен возврату без выдаваемого
значения, что является незаконным для функции, возвращающей
значение.

R.6.6.4 Оператор goto


Оператор goto безусловно передает управление на оператор,
помеченный идентификатором. Идентификатор должен быть меткой
($$R.6.1), находящейся в текущей функции.

R.6.7 Оператор описания


Оператор описания заводит в блоке новый идентификатор и имеет
вид:

     оператор-описания:
    описание

Если идентификатор, введенный с помощью описания, уже был ранее
описан во внешнем блоке, внешнее описание становится скрытым до
конца блока, после чего оно опять вступает в силу.
    Все инициализации автоматических (auto) и регистровых (register)
переменных производятся каждый раз, когда выполняется
оператор-описание. Уничтожение локальных переменных, описанных в
блоке, происходит при выходе из блока ($$R.6.6). Уничтожение
автоматических переменных, определенных в цикле, происходит
на каждом шаге цикла. Например, переменная Index j создается и
уничтожается каждый раз в течение цикла по i:

     for (int i = 0; i<100; i++)
  for (Index j = 0; j<100; j++) {
  // ...
  }

Выход из цикла или из блока или переход, минуя инициализацию
автоматических переменных, приводит к уничтожению автоматических
переменных, описанных в точке, откуда происходит переход, но не
в точке, куда происходит переход.
    Переход в блок возможен при условии, что он не приводит к
пропуску инициализации. Считается незаконным переход, обходящий
описание с явной или неявной инициализацией, кроме случаев, когда
оно находится во внутреннем блоке, который пропускается (т.е. в него
никогда не попадает управление) или переход происходит из той точки,
где уже была инициализация переменной. Например,

     void f()
     {
 // ...
 goto lx;  //ошибка: переход, минуя инициализацию
 // ...
     ly:
  X a = 1;
  // ...
     lx:
  goto ly; // нормально, за переходом будет вызов
           // деструктора для `a'
     }

 Автоматическая переменная, которая была создана при некотором
 условии, уничтожается при выполнении этого условия, и не может
 быть доступна вне проверки этого условия. Например,

 if (i)
         for (int j = 0; j<100; j++) {
         // ...
 }
 if (j!=100)  // ошибка: обращение вне условия
  // ...
  ;

  Инициализация локального объекта с классом памяти static ($$R.7.1.1)
производится прежде, чем управление  пройдет через область его
описания. Если статическая переменная инициализируется выражением,
которое не является выражением-константой, то перед первым входом
в блок происходит стандартная инициализация нулем, приведенным
к нужному типу ($$R.8.4).
    Деструктор для локального статического объекта будет вызываться
в том и только в том случае, если переменная была создана с помощью
конструктора. Деструктор должен вызываться сразу перед вызовом или
как составная часть вызова функций, заданных в atexit() ($$R.3.4).

R.6.8 Разрешение неоднозначности


Существует неоднозначность в грамматике языка, касающаяся
оператора-выражения и описания, а именно, оператор-выражение,
содержащий как самое левое подвыражение явное преобразование типа,
заданное в функциональном стиле ($$R.5.2.3), может быть не отличим от
описания, в котором первый описатель начинается со (. В таких случаях
оператор считается описанием.
    Для разрешения неоднозначности следует исследовать весь оператор,
чтобы определить является он оператором-выражением или описанием.
Так устраняется неоднозначность во многих случаях. Например, пусть
T - имя-простого-типа ($$R.7.1.6), тогда имеем

    T(a)->m = 7;       // оператор-выражение
    T(a)++;            // оператор-выражение
    T(a,5)<<c;         // оператор-выражение

    T(*e)(int);        // описание
    T(f)[];            // описание
    T(g) = {1, 2 };    // описание
    T(*d)(double(3));  // описание

  Остальные случаи представляют описания. Например,

    T(a);         // описание
    T(*b)();      // описание
    T(c)=7;       // описание
    T(d),e,f=3;   // описание
    T(g)(h,2);    // описание

   Неоднозначность здесь чисто синтаксическая, т.е. на ее
разрешение не влияет тот факт, является ли имя именем-типа или нет.
   Есть другой вид коллизии между оператором-выражением и описанием,
который разрешается требованием, чтобы описание функции в блоке
($$R.6.3) сопровождалось именем-типа, например:

    void g()
    {
      int f();  // описание
      int a;    // описание
      f();      // оператор-выражение
      a;        // оператор-выражение
   }

R.7 Описания


Описания используются для интерпретации каждого из идентификаторов;
необязательно, чтобы они сопровождались выделением памяти,
сопоставляемой с идентификатором. Описания имеют вид

       описания:
              спецификации-описания opt список-описателей opt ;
              описание-asm
              определение-функции
              спецификация-связи

Описатели в списке-описателей ($$R.8) содержат описываемые
идентификаторы. Конструкция спецификации-описания может отсутствовать
только в определении функций ($$R.8.3) или в описании функций.
Список-описателей может быть пустым, только при описании класса ($$R.9)
или перечисления ($$R.7.2), т.е. когда спецификация-описания есть
спецификация-класса или спецификация-перечисления.
Конструкция описание-asm объясняется в $$R.7.3, а спецификация-связи
в $$R.7.4. Описание происходит в определенной области видимости
($$R.3.2), правила области видимости приводятся в $$R.10.4.

R.7.1 Спецификации


В описании можно использовать следующие спецификации:

        спецификация-описания:
             спецификация-класса-памяти
             спецификация-типа
             спецификация-fct
             спецификация-шаблона-типа
             friend
             typedef
        спецификации-описания:
             спецификации-описания opt спецификация-описания

    Самая длинная последовательность конструкций спецификация-описания,
которая, возможно, является именем типа, образует в описании конструкцию
спецификации-описания. Последовательность должна быть согласованной,
что объясняется ниже. Например,

      typedef char* Pc;
      static Pc;        // ошибка: нет имени

Здесь описание static Pc является незаконным, поскольку не указано
никакого имени статической переменной типа Pc. Чтобы иметь
переменную типа int с именем Pc, необходимо задать
спецификацию-типа int, чтобы показать, что (пере)определяется
имя Pc из typedef, а не просто Pc является одним из элементов
последовательности конструкций спецификация-описания, например,

      void f(const Pc);      // void f(char* const)
      void g(const int Pc);  // void g(const int)

    Укажем, что поскольку signed, unsigned, long и short по
умолчанию трактуются как int, конструкция имя-typedef, которая
появляется после одной из перечисленных спецификаций типа,
должна задавать (пере)определяемое имя, например,

      void h(unsigned Pc);     // void h(unsigned int)
      void k(unsigned int Pc); // void k(unsigned int)

R.7.1.1 Спецификации класса памяти


Спецификации класса памяти могут быть такие:

      спецификация-класса-памяти:
           auto
           register
           static
           extern

   Спецификации auto и register могут применяться только для
имен объектов, которые описаны в блоке ($$R.6.3), или для формальных
параметров ($$R.8.3). Почти всегда спецификация auto избыточна и
используется не часто, так, auto используется, чтобы явно отделить
оператор-описание от оператора-выражения ($$R.6.2).
   Описание register является описанием auto, которое подсказывает
транслятору, что описываемые переменные будут использоваться
достаточно интенсивно. Подсказка может быть проигнорирована, и во
многих реализациях она игнорируется в том случае, когда берется
адрес переменной.
   Описание объекта считается определением, если только оно не
содержит спецификации extern и инициализации ($$R.3.1).
   Определение приводит к выделению памяти соответствующего
размера и выполнению соответствующей инициализации ($$R.8.4).
   Спецификации static и extern могут применяться только к именам
объектов или функций или к анонимным объединениям. Внутри блока
недопустимы описания функций со спецификацией static или
формальных параметров со спецификацией static или extern.
Статические члены класса описываются в $$R.9.4. Спецификация
extern недопустима для членов класса.
   Имя со спецификацией static подлежит внутреннему связыванию.
Объекты, описанные как const, подлежат внутреннему связыванию,
если только они не были описаны с внешней связью. Имя со
спецификацией extern подлежит внешнему связыванию, если только ранее
оно не было описано с внутренней связью. Имя с файловой областью
видимости и без спецификации-класса-памяти подлежит внешнему
связыванию, если только ранее оно не было описано с внутренней
связью или со спецификацией const. В смысле связывания для функций,
не являющихся членами, спецификация inline эквивалентна static
($$R.3.3). Для одного имени все его спецификации, определяющие
связывание, должны быть согласованы. Например,

   static char* f();     // f() имеет внутреннее связывание
   char* f()             // f() все еще внутреннее
      { /* ... */ }

   char* g();            // g() имеет внешнее связывание
   static char* g()      // ошибка: противоречие в связывании
      { /* ... */ }

    static int a;        // `a' имеет внутреннее связывание
    int a;               // ошибка: второе определение

    static int b;        // `b' имеет внутреннее связывание
    extern int b;        // `b' все еще внутреннее

    int c;               // `c' имеет внешнее связывание
    static int c;        // ошибка: противоречие в связывании

    extern int d;        // `d' имеет внешнее связывание
    static int d;        // ошибка: противоречие в связывании

    Имя неопределенного класса можно использовать в описании
extern. Однако, такое описание нельзя использовать прежде, чем
класс будет определен, например,

    struct S;
    extern S a;
    extern S f();
    extern void g(S);

    void h()
    {
      g(a);    // ошибка: S неопределено
      f();     // ошибка: S неопределено
    }

R.7.1.2 Спецификации функций


Некоторые спецификации можно использовать только в описании функций.

    спецификация-fct:
         inline
         virtual

  Спецификация inline подсказывает транслятору, что необходимо
произвести подстановку тела функции вместо обычной реализации
вызова функции. Подсказка может игнорироваться. В случае функций,
не являющихся членами, спецификация inline дополнительно устанавливает
для функции внутреннее связывание ($$R.3.3). Функция ($$R.5.2.2,
$$R.8.2.5), определенная в описании класса, имеет по умолчанию
спецификацию inline.
   Функция-член со спецификацией inline должна иметь в точности
такое же определение в каждой единице трансляции, где она появляется.
   Функцию-член не обязательно явно описывать со спецификацией
inline при описании класса, чтобы она трактовалась как подстановка.
Если спецификации inline не было, связывание будет внешним,
если только определение со спецификацией inline не появится перед
первым вызовом функции.

     class X {
     public:
       int f();
       inline int g();    // X::g() имеет внутреннее связывание
       int h();
     };

     void k(X* p)
     {
       int i = p->f();    // теперь X::f() внешнее связывание
       int j = p->g();
       // ...
     }

    inline int X::f()    // ошибка: вызов до определения
                         // как inline
    {
      // ...
    }

   inline int X::g()
   {
     // ...
   }

   inline int X::h()     // теперь X::h() имеет внутреннее связывание
   {
     // ...
   }

   Спецификация virtual может использоваться только в описаниях
нестатических функций-членов при описании класса (см. $$R.10.2).

R.7.1.3 Спецификация typedef


Описания со спецификацией typedef задают идентификаторы, которые
позднее могут использоваться для обозначения основных или
производных типов. Спецификация typedef недопустима в определении-функции
($$R.8.3).
    имя-typedef:
          идентификатор
В пределах области видимости ($$R.3.2) описания typedef любой
идентификатор, появляющийся в части любого из описателей,
становится синтаксически эквивалентным служебному слову и обозначает тип,
связанный с данным идентификатором, как описано в $$R.8. Таким образом,
имя-typedef является синонимом другого типа. В отличие от описания
класса ($$R.9.1) имя-typedef не добавляет нового типа. Например,
после описания

        typedef int MILES, *KLICKSP;

конструкции

    MILES distance;
    extern KLICKSP metricp;

являются законными описаниями, тип distance есть int, а у metricp
тип "указатель на int".
   С помощью typedef можно переопределить имя так, чтобы оно опять
обозначало тип, на который уже ссылалось, причем даже в той области
видимости, в которой тип был первоначально описан, например,

     typedef struct s { /* ... */ } s;
     typedef int I;
     typedef int I;
     typedef I I;

  Безымянный класс, который определяется в typedef, получает в
качестве своего имени имя, использованное в typedef, например,

     typedef struct { /* .... */ } S; // имя структуры стало S

  С помощью описания typedef нельзя переопределить имя типа,
описанного в этой же области видимости, так, чтобы оно обозначало
другой тип, например,

    class complex { /* ... */ };
    typedef int complex;   // ошибка: переопределение

  Аналогично, нельзя описывать класс с именем типа, описанного
в этой же области видимости, так, чтобы он обозначал другой
тип, например,

    typedef int complex;
    class complex { /* ... */ };  // ошибка: переопределение

  Имя-typedef, которое обозначает класс, является именем-класса
($$R.9.1). Синоним нельзя использовать после следующих префиксов:
class, struct и union, а также в именах конструкторов и
деструкторов в описании самого класса, например,

    struct S {
        S();
       ~S();
    };

    typedef struct S T;
    S a = T();    // нормально
    struct T* p;  // ошибка



R.7.1.4 Спецификация шаблона типа


Спецификация шаблона типа используется для задания семейства типов
или функций (см. $$R.14).

R.7.1.5 Спецификация friend


Спецификация friend используется для задания доступа к членам класса
(см. $$R.11.4).

R.7.1.6 Спецификация типа


К спецификации типа относятся:

     спецификация-типа:
         имя-простого-типа
         спецификация-класса
         спецификация-перечисления
         спецификация-сложного-типа
         :: имя-класса
         const
         volatile

При описании объекта служебные слова const и volatile можно добавить
к любой законной спецификации-типа. Во всех других случаях в описании
может присутствовать не более одной спецификации-типа. Объект со
спецификацией const можно инициализировать, но его значение не
должно изменяться в дальнейшем. Объект со спецификацией const, если
только он не был явно описан как extern, не подлежит внешнему
связыванию и должен инициализироваться ($$R.8.4, $$R.12.1). Целое
со спецификацией const, инициализированное выражением-константой,
может использоваться в выражении-константе ($$R.5.19). Каждый
элемент массива со спецификацией const имеет ту же спецификацию,
а каждый нестатический член, не являющийся функцией, из объекта класса
со спецификацией const сам считается const ($$R.9.3.1). Объект типа
без конструктора или деструктора, который имеет спецификацию const,
может быть помещен в память, доступную только по чтению. Попытка
записи в любую часть такого объекта или приведет к особой адресной
ситуации, или пройдет бесследно, как если бы объект не имел
спецификации const.
    Не существует не зависящего от реализации объяснения объектов со
спецификацией volatile. Она служит подсказкой транслятору избегать
слишком активной оптимизации, связанной с этим объектом, поскольку
значение объекта может изменяться способами, скрытыми от
транслятора. Каждый элемент массива со спецификацией volatile
имеет ту же спецификацию и каждый нестатический член, не являющийся
функцией, из объекта класса со спецификацией volatile сам считается
volatile ($$R.9.3.1).
   Если спецификация-типа отсутствует в описании, она считается
заданной как int.

     имя-простого-типа:
           полное-имя-класса
           уточненное-имя-типа
           char
           short
           int
           long
           signed
           unsigned
           float
           double
           void

Вместе с int нельзя задавать более одного служебного слова long
или short. Они могут использоваться и поодиночке, тогда считается,
что тип есть int. Служебное слово long может появиться вместе с
double. Вместе с char, short, int или long нельзя задавать более
одного служебного слова signed или unsigned. Они могут
использоваться и поодиночке, тогда считается, что тип есть int.
Спецификация signed указывает, что объекты типа char и битовые
поля являются знаковыми, для других целочисленных типов эта
спецификация избыточна.
    Конструкции спецификация-класса и спецификация-перечисления
определяются в $$R.9 и $$R.7.2 соответственно.

     спецификация-сложного-типа:
           служебное-слово-класса имя-класса
           служебное-слово-класса идентификатор

     служебное-слово-класса:
           class
           struct
           union

   Если задан идентификатор, спецификация-сложного-типа описывает
его как имя-класса (см. $$R.9.1).
   Если определено имя, которое описывается с помощью спецификации
union, то оно должно быть определено как объединение. Если определено
имя, которое описывается с помощью спецификации class, то оно должно
быть определено с помощью спецификаций class или struct. Если
определено имя, которое описывается с помощью спецификации struct,
оно должно быть определено с помощью спецификации class или
struct. Имена вложенных типов ($$R.9.7) должны уточняться именем
объемлющего класса:

          уточненное-имя-типа:
               имя-typedef
               имя-класса :: уточненное-имя-типа

          полное-имя-класса:
               уточненное-имя-класса
               :: уточненное-имя-класса

          уточненное-имя-класса:
               имя-класса
               имя-класса :: уточненное-имя-класса

Имя, уточненное именем-класса должно быть типом, определенным в
этом классе или в базовом классе этого класса. Как обычно, имя,
описанное в производном классе, делает невидимыми члены с этим
именем из базовых классов (см. $$R.3.2).

R.7.2 Описание перечисления


Перечисление является отдельным целочисленным типом ($$R.3.6.1)
с константами-именами. Его имя в своей области видимости становится
конструкцией имя-перечисления, т.е. служит зарезервированным словом.

          имя-перечисления:
              идентификатор

          спецификация-перечисления:
              enum идентификатор opt { список-перечисления }

          список-перечисления:
              элемент-перечисления
              список-перечисления , элемент-перечисления

          элемент-перечисления:
              идентификатор
              идентификатор = выражение-константа
Все идентификаторы из списка-перечисления считаются описанными
как константы и могут появляться всюду, где требуются константы.
Если не было элементов перечисления с =, то значения констант
начинаются с нуля и последовательно увеличиваются на единицу
по мере продвижения в списке слева направо. Если элемент
перечисления встретился с =, то его идентификатор принимает заданное
значение, а последующие идентификаторы без инициализирующей части
будут получать возрастающие значения, начиная с заданного. Значение
элемента перечисления должно быть типа int или значением, которое
можно привести к int с помощью стандартных целочисленных
преобразований ($$R.4.1).
   Имена элементов перечисления должны быть отличны от имен обычных
переменных и других элементов перечисления той же области
видимости. Значения элементов перечисления не обязаны отличаться
друг от друга. Считается, что элемент перечисления описан с момента
появления его идентификатора или инициализирующего значения,
(если оно есть). Например, в определениях

      enum { a, b, c=0 };
      enum { d, e, f=e+2 };

значения a, c, и d заданы как 0, b и e как 1, а f как 3.
    Каждое перечисление является целочисленным типом, который
отличен от всех других целочисленных типов. Типом элемента перечисления
считается данное перечисление. Значение элемента перечисления или
объекта типа перечисления преобразуется к целому с помощью
стандартных целочисленных преобразований ($$R.4.1). Например,
в следующем фрагменте:

      enum color { red, yellow, green=20, blue };
      color col = red;
      color* cp = &col;
      if (*cp == blue ) // ...

color задан как целочисленный тип, описывающий разные цвета,
col описан как объект этого типа, а cp как указатель на объект
этого типа. Возможными значениями объекта типа color являются
red, yellow, green, blue. Эти значения можно преобразовать
в целые значения 0, 1, 20 и 21. Поскольку каждое перечисление - это
отдельный тип, объекту типа color можно присваивать только значения
типа color, например,

    color c = 1;    // ошибка: несоответствие типов
                    // нет преобразования от int в color

    int i = yellow; // нормально: yellow преобразуется в int со значением 1
                    // стандартное целочисленное преобразование

Обратитесь также к $$R.18.3.
   Элементы перечисления, определенные в классе ($$R.9), относятся
к области видимости этого класса, и к ним можно обращаться извне
функций-членов этого класса только с помощью явного уточнения
именем класса ($$R.5.1). Имя самого типа перечисления локально
в этом классе ($$R.9.7), например,

      class X {
      public:
          enum direction { left='l', right='r' };
          int f(int i)
              { return i==left ? 0 : i==right ? 1 : 2; }
      };

      void g(X* p)
      {
        direction d;        // ошибка: `direction' вне
        int i;              // области видимости
        i = p->f(left);     // ошибка: `left' тоже невидим
        i = p->f(X::right); // нормально
        // ...
      }

R.7.3 Описания asm


Описание asm имеет вид:

       описание-asm:
           asm ( строка-литерал) ;

Назначение описания asm определяется реализацией. Обычно оно
используется для передачи информации от транслятора к ассемблеру.

R.7.4 Спецификации связи


С помощью спецификации-связи можно связать ($$R.3.3) фрагменты
программ на С++ и на другом языке:

     спецификация-связи:
         extern строка-литерал { список-описаний opt }
         extern строка-литерал описание

     список-описаний:
         описание
         список-описаний описание

Требуемое связывание задается с помощью строки-литерала. Ее назначение
определяется реализацией. Но во всех реализациях должно быть
предусмотрено связывание с функцией на языке С ("С") и с функцией
на языке С++ ("С++"). По умолчанию связывание задается как "С++",
например,

      complex sqrt(complex);   // по умолчанию связывание с C++
      extern "C" {
          double sqrt(double); // связывание с C
      }

   Спецификации связи могут быть вложенными. Спецификация связи
не задает область видимости. Спецификация-связи может встретиться
только в файловой области видимости ($$R.3.2). Спецификация-связи
для класса относится к объектам, описанным в нем, и функциям, не
являющимся членами. Спецификация-связи, относящаяся к некоторой
функции, относится и ко всем объектам и функциям, описанным в ней.
Описание связи, содержащее неизвестную для реализации строку,
считается ошибочным.
   Если функция имеет более одной спецификации-связи, то они должны
быть согласованы, т.е. задавать одну и ту же строку-литерал.
Описание функции без указания спецификации-связи не должно
предшествовать первому указанию спецификации связи для этой функции.
Функция может быть описана без указания спецификации связи даже
после явного указания спецификации связи, но связывание, явно заданное
в более раннем описании, не будет устранено таким описанием функции.
   Из множества перегруженных функций ($$R.13) с данным именем
не более одной может иметь связывание с языком С, см. $$R.7.4.
   Связывание можно установить для объектов, например:

     extern "C" {
        // ...
        _iobuf_iob[_NFILE];
        // ...
        int _flsbuf(unsigned,_iobuf*);
        // ...
     }

Когда задается спецификация связи, то функции и объекты можно описать как
статические внутри { }. Для таких функций или объектов команда
связывания игнорируется. Иначе, функция, описанная при задании связи,
трактуется, как если бы она была явно описана как extern, например,
ниже второе описание ошибочно ($$R.7.1.1):

     extern "C" double f();
     static double f();     // ошибка

Объект, описанный внутри конструкции

     extern "C" {  /* ... */ }

все же считается определенным, а не просто описанным.
    Связывание объектов на С++ с объектами, определенными на других
языках, так же как и обратное связывание, зависит от языков и
реализации. Такое связывание возможно только в том случае, когда
алгоритмы размещения объектов в памяти являются достаточно схожими
для двух языков.
    Если для задания связи в строке-литерале из спецификации-связи
используется имя языка программирования, то рекомендуется, чтобы
написание этого имени копировалось из документа, определяющего данный
язык, например, Ada (а не ADA) и Fortran (а не FORTRAN).

R.8 Описатели


Список-описателей, фигурирующий в описании, - это последовательность
через запятую описателей, каждый из которых может иметь
инициализатор.

      список-описаний:
           описатель-с-инициализатором
           список-описаний , описатель-с-инициализатором

      описатель-с-инициализатором:
           описатель инициализатор opt

Описание состоит из двух частей: спецификации (спецификация-описания;
см. $$R.7.1) и описателей (список-описателей). Спецификации задают
основной тип, класс памяти или другие свойства описываемых объектов
и функций. Описатели задают имя этих объектов и функций, а также,
возможно, изменяют тип с помощью таких операций, как * (указатель на)
и () (функция возвращающая). В описателе также можно задать начальные
значения, инициализация обсуждается в $$R.8.4  и $$R.12.6.
    Описатели имеют такой синтаксис:

    описатель:
      имя-в-описателе
      операция-ptr описатель
      описатель (список-описаний-параметров) список-спецификаций-cv opt
      описатель [ выражение-константа opt]
      ( описатель )

   операция-ptr:
      * список-спецификаций-cv opt
      & список-спецификаций-cv opt
      полное-имя-класса :: * список-спецификаций-cv opt

   список-спецификаций-cv:
      const
      volatile

   имя-в-описателе:
       имя
       имя-класса
       ~имя-класса
       имя-typedef
       уточненное-имя-типа

Конструкция имя-класса имеет определенное назначение при описании
класса с этим именем, она же используется как уточнение в операции ::
для разрешения коллизий в области видимости ($$R.12.1, $$R.12.4).

R.8.1 Имена типов


Имя типа необходимо указывать при задании операции явного
преобразования типа или в качестве параметра в операциях sizeof
или new. Для этого служит конструкция имя-типа, которая синтаксически
эквивалентна описанию объекта или функции этого типа, в котором
отсутствует имя объекта или функции.

     имя-типа:
       список-спецификаций-типа абстрактный-описатель opt

     список-спецификаций-типа:
       спецификация-типа список-спецификаций-типа

     абстрактный-описатель:
       операция-ptr абстрактный-описатель opt

абстрактный-описатель opt ( список-описаний-параметров ) список-спецификаций cv opt
       абстрактный-описатель opt [ выражение-константа opt ]
       ( абстрактный-описатель )

Можно однозначно указать, в каком месте абстрактного-описателя
нужно добавить идентификатор, чтобы конструкция стала описателем,
допустимым в описании. Тогда поименованный тип будет тем же, что и
тип гипотетического идентификатора. Например, описания

        int             // int i
        int *           // int *pi
        int *[3]        // int *p[3]
        int (*)[3]      // int (*p3i)[3]
        int *()         // int *f()
        int (*)(double) // int (*pf)(double)

задают соответственно такие типы: "целое", "указатель на целое",
"массив из 3 указателей на целое", "указатель на массив из 3 целых",
"функция без параметров, возвращающая указатель на целое",
"указатель на функцию с параметром типа double, возвращающую целое".

R.8.1.1 Устранение неоднозначности


Неоднозначность, отмеченная в $$R.6.8, которая возникает из-за сходства
между приведением, заданным в функциональном стиле, и описанием,
может также появиться в контексте описания. В этом контексте она
проявляется как сходство между описанием функции, в котором есть
избыточные скобки вокруг имени параметра, и описанием объекта, в
котором в качестве инициализатора используется операция приведения,
заданная в функциональном стиле. Как и для операторов, неоднозначность
устраняется правилом, согласно которому следует считать описанием любую
конструкцию, которая может служить таковым. Можно явно устранить
неоднозначность в описании или с помощью приведения, заданного не в
функциональном стиле, или с помощью операции = для обозначения
инициализации, например,

     struct S {
        S(int);
     };

    void foo(double a)
    {
      S x(int(a));   // описание функции
      S y((int)a);   // описание объекта
      S z = int(a);  // описание объекта
    }

R.8.2 Смысл описателей


Список описателей следует после (возможно пустого) списка
спецификаций-описания ($$R.7.1). Каждый описатель содержит в точности
одно имя-из-описателя, которое задает описываемый идентификатор.
Если не считать описаний некоторых специальных функций ($$R.12.3,
$$R.13.4), имя-из-описателя является просто идентификатором.
Спецификации auto, static, extern, register, friend, inline, virtual
или typedef относятся непосредственно к каждому имени-из-описателя из
списка описателей. Тип каждого имени-из-описателя определяется как
спецификацией-описания ($$R.7.1), так и его описателем.
     Таким образом, описание некоторого идентификатора имеет вид

         T D

где T обозначает тип, а D - описатель. Если в описании D есть
идентификатор без скобок, то тип этого идентификатора есть T.
     В описании, где D имеет вид

         ( D1 )

тип D1 такой же, как и тип D. Наличие скобок не меняет типа заключенного
в них имени-из-описателя, но для сложных описателей оно может повлиять
на порядок применения операций.

R.8.2.1 Указатели


В описании T D, в котором D имеет вид

      * список-спецификаций-cv opt D1

тип описываемого идентификатора есть
"... список-спецификаций-cv указатель на T". Конструкция
список-спецификаций-cv относится к указателю, а не к указуемому
объекту.
   Например, в описаниях

      const ci = 10, *pc = &ci, *const cpc = pc;
      int i *p, *const cp = &i;

определяются: ci как константа целое; pc как указатель на константу
целое; cpc как константа указатель на константу целое; i как целое;
p как указатель на целое; и cp как константа указатель на целое.
После инициализации значения ci, cpc и cp не могут быть изменены.
Значение pc можно изменять так же, как и значение объекта, на который
указывает cp. Приведем примеры допустимых операций:

      i = ci;
      *cp = ci;
      pc++;
      pc = cpc;
      pc = p;

Недопустимы следующие операции:

      ci = 1;    // ошибка
      ci++;      // ошибка
      *pc = 2;   // ошибка
      cp = &ci;  // ошибка
      cpc++;     // ошибка
      p = pc;    // ошибка

Каждая из этих операций недопустима или потому, что она изменяет значение
объекта, описанного со спецификацией const, или потому, что делает
такое изменение возможным позднее с помощью указателя, настроенного
на объект без спецификации const.
    Аналогична ситуация со спецификацией volatile.
    Обратитесь к $$R.5.17 и $$R.8.4.
    Нельзя описывать указатели на ссылки ($$R.8.2.2) или указатели
на битовые поля ($$R.9.6).

R.8.2.2 Ссылки


В описании T D, в котором D имеет вид

    & список-спецификаций-cv opt D1

тип описываемого идентификатора есть
"...список-спецификаций-cv ссылка на T". Тип void& недопустим.
    Например, во фрагменте

    void f(double& a) { a += 3.14; }
    // ...
       double d = 0;
       f(d);

a описывается как параметр, являющийся ссылкой, поэтому вызов
f(d) приведет к увеличению d на 3.14. Во фрагменте

    int v[20];
    // ...
    int& g(int i) { return v[i]; }
    // ...
    g(3) = 7;

описывается: функция g() возвращает ссылку на целое; поэтому
оператор g() = 7; присвоит 7 четвертому элементу массива v.
Рассмотрим следующий программный фрагмент:

   struct link {
      link* next;
   };
   link* first;

   void h(link*& p)  // `p' ссылка на указатель
   {
     p->next = first;
     first = p;
     p = 0;
   }

   void k()
   {
     link* q = new link;
     h(q);
   }

Здесь p описано как ссылка на указатель на link, поэтому вызов h(q)
не изменит значение q, равное 0, см. также $$R.8.4.3.
   Недопустимы ссылки на ссылки, ссылки на битовые поля ($$R.9.6),
массивы ссылок и указатели на ссылки. Описание ссылки должно содержать
инициализатор ($$R.8.4.3), за исключением тех случаев, когда описание
содержит явную спецификацию extern ($$R.7.1.1), или является описанием
члена класса ($$R.9.2) при описании самого класса, или является
описанием параметра или возвращаемого типа ($$R.8.2.5),
см. также $$R.3.1.

R.8.2.3 Указатели на члены


В описании T D, в котором D имеет вид
    полное-имя-класса :: * список-спецификаций-cv opt D1
тип описываемого идентификатора есть
"... список-спецификаций-cv указатель на член класса полное-имя-класса типа T".
Например, во фрагменте

     class X {
     public:
        void f(int);
        int a;
     };

     int X::* pmi = &X::a;
     void (X::* pmf)(int) = &X::f;

pmi и pmf описываются как указатель на член X типа T и указатель на
член X типа void(int) соответственно. Эти объекты можно использовать
так:

    X obj;
    // ...
    obj.*pmi = 7;   // присвоить 7 члену obj типа int
    (obj.*pmf)(7);  // вызвать функцию-член obj
                    // с параметром 7

   Отметим, что указатель на член нельзя настроить на статический
член класса ($$R.9.4), см. также $$R.5.5 и $$R.5.3.

R.8.2.4 Массивы


В описании T D, в котором D имеет вид

    D1 [ выражение-константа opt ]

описывается идентификатор типа " ... массив T". Если
выражение-константа присутствует ($$R.5.19), то оно должно иметь
целочисленный тип и значение, большее 0. Это выражение задает число
элементов массива. Если значение выражения-константы есть N, то
массив имеет N элементов с индексами от 0 до N-1.
   Массив можно образовывать из: одного из основных типов (за исключением
void), указателя, указателя на члены, класса, перечисления или из
другого массива.
   Если подряд идут несколько спецификаций "массив ...", образуется
многомерный массив, причем выражение-константа, задающее границы
массива, может отсутствовать только для первого массива. Такое умолчание
полезно в случае параметров функции типа массив, а также когда массив
является внешним, а его определение, с которым связано резервирование
памяти, находится в другом месте. Первое выражение-константа может
быть пропущено и в том случае, если за описателем следует
список-инициализаторов ($$R.8.4). Тогда размер массива определяется
числом элементов, приведенных в инициализаторе ($$R.8.4.1).
    В описании

        float fa[17], *afp[17];

описаны массив чисел типа float и массив указателей на числа типа float,
а в описании

        static int x3d[3][5][7];

описан статический трехмерный массив целых размера 3x5x7. Строго
говоря, x3d является массивом из трех элементов, каждый из которых
есть массив из пяти массивов, а каждый из последних является массивом
из семи целых. В выражении допустимо появление любого из следующих
выражений: x3d, x3d[i], x3d[i][j], x3d[i][j][k].
    Если в выражении участвует идентификатор типа массив, то, исключая
случаи операнда в операциях sizeof или & и инициализатора для
ссылки ($$R.8.4.3), его тип преобразуется в указатель на первый
элемент массива. Несмотря на это преобразование, массивы не являются
изменяемыми адресами. Если не считать случай использования массива
при описании класса ($$R.13.4.5), операция индексации определяется
так, что E1[E2] совпадает с *((E1) + (E2)). С учетом правил
преобразования типов для операции +, если E1 есть массив, а E2
целое, то E1[E2] указывает на E2-элемент из E1. Поэтому, несмотря
на свой асиметричный вид, индексация - коммутативная операция.
    Аналогичное правило действует и для многомерных массивов. Если
E - n-мерный массив размера ixjx...xk, то в выражении он
преобразуется в указатель на (n-1)-мерный массив размера jx...xk.
Если к этому указателю явно или неявно в результате индексации применяется
операция *, указуемый (n-1)-мерный массив сам немедленно преобразуется
в указатель.
    Например, рассмотрим описание

    int x[3][5];

Здесь описан массив из 3x5 целых. Если в выражении появляется x, то
оно преобразуется в указатель на первый массив из пяти целых.
Если в выражении появляется x[i], что эквивалентно *(x+i), в начале
x преобразуется в указатель, как было сказано выше, затем x+i
преобразуется к типу x, для чего необходимо i умножить на размер объекта,
на который указывает x, т.е. на размер пяти целых. Затем происходит
сложение и применяется косвенность, после чего получим массив (из пяти
целых), который в свою очередь преобразуется в указатель на первое из
целых. Если есть еще одна индексация, процесс повторяется, и на этот раз
мы получим в результате целое.
   Из всего этого следует, что массивы В С++ хранятся по строкам
(последний индекс изменяется быстрее всего), а значение первого
индекса из описания позволяет вычислить размер памяти, необходимой
для массива, однако при вычислении индексного выражения первый индекс
роли не играет.

R.8.2.5 Функции


В описании T D, в котором D имеет вид

   D1 (список-описаний-параметров ) список-спецификаций-cv opt

описываемый идентификатор имеет тип
"...список-спецификаций-cv функция с параметрами типа
список-описаний-параметров возвращающая T".

   список-описаний-параметров:
        список-описаний-парам opt ... opt
        список-описаний-парам , ...

   список-описаний-парам:
        описание-параметра
        список-описаний-парам , описание-параметра

   описание-параметра:
        спецификации-описания описатель
        спецификации-описания описатель = выражение
        спецификации-описания абстрактный-описатель opt
        спецификации-описания абстрактный-описатель opt = выражение

   Если список-описаний-параметров завершается эллипсисом (...),
про число параметров известно только то, что оно больше или равно
числа заданных параметров, если список параметров пуст, то функция
параметров не имеет. Список параметров void эквивалентен пустому
списку параметров. Не считая этого случая, void не может быть типом
параметра (хотя типы, получаемые из void, такие как void*, допустимы).


R.8.3 Определения функций


Определения функций имеют вид

  определение-функции:
спецификации-описания opt описатель инициализатор-ctor тело-функции

  тело-функции:
     составной-оператор

Конструкция описатель из определения-функции должна содержать описатель
вида

     D1 ( список-описаний-параметров ) список-спецификаций-cv  opt

в соответствии с определениями из $$R.8.2.5
     Формальные параметры относятся к области видимости самого
большого блока тела-функции.
    Приведем пример полного определения функции.

      int max( int a, int b, int c)
      {
        int m = (a > b) ? a : b;
         return (m > c) ? m : c;
      }

Здесь int представляет спецификации-описания, max(int a, int b, int c)
- описатель, а { /* ... */ } - тело-функции.
   Конструкция инициализатор-ctor используется только в конструкторах,
см. $$R.9.3.1 и $$R.12.6.
   Конструкция список-спецификаций-cv может участвовать:
в описании нестатической функции-члена, в определении нестатической
функции-члена или в описании указателя на функцию-член, см. $$R.9.3.1.
Она относится к типу функции.
   Отметим, что неиспользуемым формальным параметрам имена можно
не давать, например,

     void print(int a, int)
     {
       printf("a = %d\n",a);
     }

R.8.4 Инициализаторы


За описателем может идти начальное значение описываемого идентификатора.

   инициализатор:
         = выражение-присваивания
         = { список-инициализаторов , opt }
         ( список-выражений )

   список-инициализаторов:
         выражение-присваивания
         список-инициализаторов , выражение-присваивания
         { список-инициализаторов , opt }

  Автоматические, регистровые, статические и внешние переменные
можно инициализировать произвольными выражениями, содержащими
константы и описанные ранее переменные и функции.

     int f(int);
     int a = 2;
     int b = f(a);
     int c(b);

  Указатель типа const T*, т.е. указатель на константу T, может
инициализироваться указателем типа T*, но инициализация для указателей
в обратном порядке незаконна. Объекты типа T можно инициализировать
объектами типа T независимо от использования спецификаций const или
volatile в типах инициализируемой переменной или инициализатора,
например,

     int a;
     const int b = a;
     int c = b;

     const int* p0 = &a;
     const int* p1 = &b;
     int* p2 = &b;        // ошибка: указатель без const
                          // настраивается на объект const
     int *const p3 = p2;
     int *const p4 = p1;  // ошибка: указатель без const
                          // настраивается на объект const
     const int* p5 = p1;

Здесь причина обеих ошибок одна: если допустить подобную инициализацию,
она позволит изменять с помощью указателя без соответствующей
спецификации значение чего-то, что было описано как const.
   На выражения для стандартных значений параметров накладывается
больше ограничений, см. $$R.8.2.6.
  Инициализация объектов классов с помощью конструкторов описывается
в $$R.12.6.1. Копирование объектов классов описывается в $$R.12.8.
Порядок инициализации статических объектов определяется в $$R.3.4
и $$R.6.7.
  Гарантируется, что переменные статического класса памяти ($$R.3.5),
которые не были инициализированы, в качестве начального значения
получат 0, приведенный к нужному типу. То же справедливо для статических
членов объектов класса. Начальные значения автоматических и
регистровых переменных, которые не были инициализированы, неопределены.
  Если инициализатор относится к указателю или объекту арифметического
типа, он состоит из одного выражения (возможно в скобках). В качестве
начального значения объекта берется значение выражения, происходят
такие же преобразования типа, как и в случае присваивания.
  Заметим, что поскольку () не является инициализатором,
описание

    X a();

задает не объект a типа класс X, а является описанием функции без
параметров, возвращающей X.
   Инициализатор для статического члена принадлежит области
видимости члена класса, например,

      int a;

      struct X {
         static int a;
         static int b;
      };

      int X::a = 1;
      int X::b = a;  // X::b = X::a

R.8.4.1 Агрегат


Агрегатом называется массив или объект типа класс ($$R.9), не имеющий
конструкторов ($$R.12.1), частных или защищенных членов ($$R.11),
базовых классов ($$R.10) и виртуальных функций ($$R.10.2). Если
агрегат инициализируется, то инициализатором должен быть
список-инициализаторов, который состоит из заключенного в фигурные
скобки списка, разделенного запятыми, инициализаторов для членов
агрегата. Инициализаторы идут в возрастающем порядке индексов или
членов агрегата. Если агрегат содержит вложенные агрегаты, это
правило применяется рекурсивно для членов вложенных агрегатов. Если
инициализаторов в списке меньше, чем членов агрегата, то он
дополняется нулевыми значениями соответствующих типов.
    Например, в следующем фрагменте

       struct S { int a; char* b; int c; }
       S ss = { 1, "asdf" };

ss.a инициализируется значением 1, ss.b - "asdf", а ss.c - 0.
    Кроме того, агрегат, являющийся классом, можно инициализировать
объектом этого класса или класса, являющегося общим производным
от него ($$R.12.8).
    Фигурные скобки разбираются следующим образом. Если
список-инициализаторов начинается левой фигурной скобкой, то
список инициализаторов, разделенных запятыми, задает
значения членам агрегата, причем считается ошибкой, если
инициализаторов больше, чем членов. Иначе, если список-инициализаторов
или вложенный агрегат не начинается левой фигурной скобкой, то
из списка используется такое число элементов, которое нужно для
инициализации членов текущего агрегата; все оставшиеся элементы
используются для инициализации членов следующего агрегата, в который
вложен текущий агрегат.
   Например, в определении

       int x[] = { 1, 3, 5 };

массив x инициализируется как одномерный массив из трех элементов,
поскольку размер массива не указан, и приведено три инициализатора.
   Приведем пример инициализации с полной скобочной структурой.

       float y[4][3] = {
          { 1, 3, 5 },
          { 2, 4, 6 },
          { 3, 5, 7},
       };

Здесь значения 1, 3, 5 инициализируют первую строку массива y[0],
т.е. y[0][0], y[0][1] и y[0][2]. Аналогично, следующие две строки
инициализируют y[1] и y[2]. Инициализаторы приведены не полностью,
поэтому y[3] инициализируется нулями. Точно такого же результата
можно достичь с помощью такой инициализации:

       float y[4][3] = {
          1, 3, 5, 2, 4, 6, 3, 5, 7,
       };

Последний (самый правый) индекс изменяется быстрее всего.
    В последнем примере инициализатор для y начинается левой фигурной
скобкой, но для y[0] скобки не задано, поэтому из списка используется
три элемента, также по три последовательных элемента используется для
y[1] и y[2]. В следующем примере

       float y[4][3] = {
       { 1 }, { 2 }, { 3 }, { 4 }
       };

инициализируется первый столбец y (который рассматривается как
двумерный массив), а остальные столбцы принимают значение 0.
     Инициализация массива объектов типа класс с помощью конструкторов
описывается в $$R.12.6.1.
     Инициализатор для объединения без конструктора должен быть
или отдельным выражением типа объединения, или заключенным в фигурные
скобки, инициализатором первого члена объединения, например,

       union u { int a; char* b; };

       u a = { 1 };
       u b = a;
       u c = 1;             // ошибка
       u d = { 0, "asdf" }; // ошибка
       u e = { "asdf" };    // ошибка

    Число инициализаторов не должно превышать числа членов или
элементов, которые инициализируются. Например, следующая
инициализация ошибочна:

       char cv[4] = { 'a', 's', 'd', 'f', 0 };  // ошибка

R.8.4.2 Символьные массивы


Массив символов (неважно, знаковых или беззнаковых) можно
инициализировать строкой-литералом: символы строки последовательно
инициализируют элементы массива. Следующее определение дает пример
символьного массива, элементы которого инициализируются строкой:

       char msg[] = "Syntax error on line %s\n";

Заметим, что поскольку '\n' задает один символ, и поскольку добавляется
завершающий символ '\0', sizeof(msg) равно 25.
    Нельзя задавать больше инициализаторов, чем есть элементов в массиве,
поэтому следующий пример ошибочен: здесь нет места для подразумевающегося
символа конца строки ('\0'):

       char cv[4] = "asdf";  // ошибка

R.8.4.3 Ссылки


Переменная, описанная как T&, т.е. "ссылка на тип T" ($$R.8.2.2),
должна инициализироваться объектом типа T или объектом, который
можно преобразовать к типу T, например,

       void f()
       {
         int i;
         int& r = i;  // `r' ссылается на `i'
         r = 1;       // `i' принимает значение 1
         int* p = &r; // `p' указывает на `i'
         int& rr = r; // `rr' ссылается на то, на что ссылалось `r',
                      //  т.е. на `i'
       };

   Ссылку после инициализации нельзя изменять так, чтобы она
обозначала другой объект. Отметим, что инициализация ссылки
трактуется совсем не так, как присваивание ссылке. Передача
параметра ($$R.5.2.2) и операция возврата значения функции ($$R.6.6.3)
считаются инициализацией.
   Инициализатор для ссылки можно опускать только в описании
параметра ($$R.8.2.5), в описании возвращаемого функцией типа,
в описании члена класса при описании самого класса ($$R.9.2) и там,
где явно использована спецификация extern, например,

       int& r1;        // ошибка: нет инициализации
       extern int& r2; // нормально

   Если инициализатор для ссылки на тип T является адресом типа T
или типом, производным от T ($$R.10), для которого T служит
доступным базовым типом ($$R.4.6), ссылка будет обозначать значение,
заданное инициализатором. Иначе, в том и только том случае, когда
ссылка обозначает объект со спецификацией const, будет создан объект
типа T  и проинициализирован значением, заданным инициализатором.
Теперь ссылка играет роль имени этого объекта, например,

        double d = 1.0;

        double& rd = d;        // rd ссылается на `d'
        const double& rcd = d; // rcd ссылается на `d'

        double& rd2 = 1;       // ошибка: несоответствие типа
        const double& rcd2 = 1;// rcd2 ссылается на временный объект
                               // со значением `1'

   Ссылку на volatile T можно инициализировать объектом типа
volatile T или просто T, но не const T. Ссылку на const T можно
инициализировать const T, просто T или чем-то, что можно преобразовать
в тип T, но не volatile T. Ссылку на тип T (без const или volatile)
можно инициализировать только объектом типа T.
   Время жизни временного объекта, созданного при описанной
инициализации, определяется текущей областью видимости, в которой
он был создан ($$R.3.5). Отметим, что ссылку на класс B можно
инициализировать объектом класса D при условии, что В является
однозначно определенным и доступным базовым классом для D (тогда
говорят, что "D есть B"), см. $$R.4.7.

R.9 классы


Класс есть тип. Его имя используется как имя-класса ($$R.9.1), т.е.
становится зарезервированным словом в его области видимости.

       имя-класса:
           идентификатор

Для образования конструкции имя-класса используются спецификации-класса
и спецификации-сложного-типа ($$R.7.1.6). Объект класса состоит из
последовательности (возможно пустой) членов.

    спецификация-класса:
      заголовок-класса { список-членов opt }

    заголовок-класса:
      служебное-слово-класса идентификатор opt спец-базовых opt
      служебное-слово-класса имя-класса спец-базовых opt

    служебное-слово-класса:
       class
       struct
       union

   Имя класса можно использовать в качестве конструкции имя-класса
даже в списке-членов самого этого класса. Обычно спецификацию-класса
называют описанием класса. Класс считается определенным, как только
появится спецификация-класса, несмотря на то, что его функции-члены
могут быть еще неопределены.
   Объекты пустого класса имеют ненулевой размер.
   Объекты типа класс можно присваивать, передавать в качестве
параметров функций и получать в качестве значения, возвращаемого
функцией (за исключением объектов тех классов, для которых копирование
ограничено, см. $$R.12.8). Другие возможные операции, такие, как
сравнение на равенство, могут определяться пользователем, см. $$R.13.4.
    Структурой называется класс, описанный со служебным-словом-класса
struct; ее члены и базовые классы ($$R.10) считаются общими по
определению ($$R.11). Объединением называется класс, описанный со
служебным-словом-класса union; его члены считаются общими по
определению, и в любой момент времени объединение содержит только
один член ($$R.9.5).

R.9.1 Имена класса


Описание класса порождает новый тип. Например, ниже описываются
три переменные трех различных типов:

      struct X { int a; };
      struct Y { int a; };
      X a1;
      Y a2;
      int a3;

Отсюда следует, что такие присваивания приводят к несоответствию
типов:

      a1 = a2;   // ошибка: Y присваивается X
      a1 = a3;   // ошибка: int присваивается X

Ниже описывается перегрузка ($$R.13) функции f(), а не просто
повторное описание той же функции:

      int f(X);
      int f(Y);

По той же причине нельзя дважды определять класс, это видно из
примера ниже, где дважды определен S:

      struct S { int a; };
      struct S { int a; };  // ошибка, повторное определение

   Описание класса включает имя класса в ту область видимости, внутри
которой оно произошло, и закрывает любой класс, объект, функцию или
другое описание этого имени в объемлющей области видимости ($$R.3.2).
Если имя класса описано в такой области видимости, где уже был
описан объект с таким же именем, функция или элемент перечисления, то
обращаться к классу можно только с помощью конструкции
спецификация-сложного-типа ($$R.7.1.6), например:

      struct stat {
         // ...
      };

      stat gstt;              // просто `stat' используется для
                              // определения переменной
      int stat(struct stat*); // переопределение `stat' как функции

      void f()
      {
         struct stat* ps;     // нужен префикс struct
                              // для задания структуры stat
         // ...
         stat(ps);            // вызов stat()
         // ...
      }

Конструкция спецификация-сложного-типа вместе со
служебным-словом-класса, но без описания объекта или функции также
может служить для задания имени класса, как и описание класса, однако
в этом случае класс не считается определенным, например:

      struct s { int a; };

      void g()
      {
        struct s;  // скрывает глобальную структуру `s'
        s* p;      // используется локальная структура `s'
        struct s { char* p; };  // описание локальной структуры `s'
      }

Такие правила позволяют классам ссылаться друг на друга при их
описании, пример,

      class vector;

      class matrix {
          // ...
          friend vector operator*(matrix&, vector&);
      };

     class vector {
         // ...
          friend vector operator*(matrix&, vector&);
     };

Описание friend (дружественные функции) обсуждается в $$R.11.4, а
функция operator в $$R.13.4. Если класс, указанный как друг, пока
еще не описан, его имя считается принадлежащим той же области
видимости, в которой находится имя класса, содержащего описание
friend ($$R.11.4).
    В описании объектов или функций можно также использовать
конструкцию спецификация-сложного-типа ($$R.7.1.6). Ее использование
отличается от описания класса тем, что если класс, чье имя указано
в спецификации, находится в текущей области видимости, то имя из
этой спецификации будет ссылаться на него, например:

      struct s { int a; }

      void g()
      {
         struct* s p = new s;  // обращение к глобальной `s'
         p->a = 1;
      }

  Имя считается описанным сразу же после появления его идентификатора
в описании. Отсюда следует, что в описании

      class A * A;

A в начале задается, как имя класса, а затем оно переопределяется
как имя указателя на объект этого класса, поэтому для обозначения этого
класса следует использовать спецификацию-сложного типа class A. Такое
"трюкачество" с именами может вызвать недоумение, и лучше его избегать.
 Конструкция имя-typedef ($$R.7.1.3) обозначает класс и считается
именем-класса, см. также $$R.7.1.3.

R.9.2 Члены класса


   список-членов:
      описание-члена список-членов opt
      спецификация-доступа : список-членов opt

   описание-члена:
      спецификации-описания opt список-описателей-членов opt ;
      определение-функции ; opt
      уточненное-имя ;

   список-описателей-членов:
      описатель-члена
      список-описателей-членов , описатель-члена

   описатель-члена:
      описатель спецификация-чистой opt
      идентификатор opt : выражение-константа

   спецификация-чистой:
      = 0

  С помощью конструкции список-членов можно описать данные, функции,
классы, элементы перечисления ($$R.7.2), битовые поля, друзей
($$R.11.4) и имена типов ($$R.7.1.3, $$R.9.1). Кроме того,
список-членов может содержать описания, устанавливающие доступ к
именам членов, см. $$R.11.3. Никакой член не может быть дважды
описан в списке-членов. Список-членов определяет все множество
членов данного класса, т.е. нельзя добавить еще один член в каком-либо
другом описании.
    Отметим, что одно имя может обозначать несколько функций-членов
при условии, что их типы достаточно отличаются друг от друга ($$R.13).
Укажем, что описатель-члена не может содержать инициализатора ($$R.8.4).
Инициализация члена возможна с помощью конструктора, см. $$R.12.1.
    Член не может иметь спецификацию auto, extern или register.
    Конструкция спецификации-описания может отсутствовать только в
описании функции. Конструкция список-описателей-членов может
опускаться только после конструкций спецификация-класса,
спецификация-перечисления или спецификация-описания, если последняя
имеет вид friend спецификация-сложного-типа. Конструкция
спецификация-чистой используется только при описании виртуальной
функции ($$R.10.2).
    Если члены являются объектами классов, то эти классы должны
быть ранее описаны. В частности, класс C1 не может содержать объект
класса C1, но может содержать указатель или ссылку на класс C1.
Если в типе нестатического члена используется массив, то все
размеры всех индексов массива должны быть указаны.
    Приведем простой пример описания класса:

    struct tnode {
        char tword[20];
        int count;
        tnode *left;
        tnode *right;
    };

Здесь класс содержит массив из двадцати символов, целое и два
указателя на ту же структуру. После появления такого описания
следующее:

    tnode s, *sp;

задает s как объект типа tnode и sp как указатель на tnode. С учетом
этих описаний s->count обозначает член count структуры, на которую
указывает sp; s.left обозначает указатель left на поддерево
структуры s; s.right->tword[0] обозначает первый символ члена
tword поддерева структуры s, на которую указывает right.
    Нестатические члены класса, представляющие данные и описанные
подряд и без использования спецификации-доступа, размещаются внутри
объекта типа класс так, что позже описанные члены имеют большие адреса.
Порядок размещения таких членов, если их описание перемежается
описаниями со спецификацией-доступа, зависит от реализации ($$R.11.1).
Принятые в реализации правила выравнивания могут привести к тому,
что два соседних члена не будут располагаться сразу друг за другом.
К этому же могут привести правила выделения памяти для виртуальных
функций ($$R.10.2) и виртуальных базовых классов ($$R.10.1);
см. также $$R.5.4.
    Функция-член ($$R.9.3), имя которой совпадает с именем класса,
является конструктором ($$R.12.1). Имя статического члена данных,
элемента перечисления, члена безымянного объединения или вложенного
типа не может совпадать с именем класса.

R.9.3 Функции-члены


Функция, описанная как член (без спецификации friend $$R.11.4),
называется функция-член и вызывается в соответствии с синтаксисом
члена класса ($$R.5.2.4), например:

      struct tnode {
         char tword[20];
         int count;
         tnode *left;
         tnode *right;
         void set(char*, tnode* l, tnode *r);
      };

Здесь set является функцией-членом и может вызываться так:

      void f(tnode n1, tnode n2)
      {
         n1.set("abc",&n2,0);
         n2.set("def",0,0);
       }

   Считается, что определение функции-члена принадлежит области
видимости ее класса. Это означает, что в функции-члене (если она
нестатическая, $$R.9.4) можно непосредственно использовать имена
членов ее класса. В статической функции-члене можно непосредственно
использовать имена только статических членов, элементов перечисления
и вложенных типов. Если определение функции-члена находится вне
описания класса, ее имя следует уточнить именем класса с помощью
операции ::, например:

       void tnode::set(char* w, tnode* l, tnode* r)
       {
         count = strlen(w)+1;
         if (sizeof(tword)<=count)
            error("tnode string too long");
         strcpy(tword,w);
         left = 1;
         right = r;
       }

Обозначение tnode::set указывает, что функция set является членом и
находится в области видимости класса tnode. Имена членов tword,
count, left и right относятся к членам того объекта, с именем
которого вызывалась Поэтому в вызове n1.set("abc",&n2,0) tword
обозначает n1.tword, а в вызове n2.set("def",0,0) tword обозначает
n2.tword. Функции strlen, error и strcpy должны быть описаны где-то
в программе.
    Члены можно определять ($$R.3.1) вне описания класса; если в
описании класса они были описаны, но не определены, их не следует
описывать заново, см. $$R.3.3. После определения класса
функции-члены этого класса можно использовать при описании друзей.
Всякая вызываемая в программе функция-член должна иметь в точности
одно определение.
    Результат вызова нестатической функции-члена ($$R.9.4) класса X,
когда она вызывается не с объектом класса X, неопределен.

R.9.3.1 Указатель this


В нестатической ($$R.9.3) функции-члене служебное слово this обозначает
указатель на объект, с которым эта функция вызывалась. В функции-члене
класса X тип this есть X *const, если только функция-член не описана
со спецификацией const или volatile; для этих случаев this имеет
тип const X *const или volatile X *const соответственно. Если
функция описана с указанием const и volatile, то тип this будет
const volatile X *const, см. также $$R.18.3.3. Приведем пример:

     struct s {
        int a;
        int f() const;
        int g() { return a++; }
        int h() const { return a++; }  // ошибка
     };

     int s::f() const { return a; }

Операция a++ в теле функции s::h ошибочна, поскольку с ее помощью
делается попытка изменить объект (часть его), с которым вызывалась
функция s::h(). Это недопустимо для функции-члена, описанной со
спецификацией const, т.к. this является указателем на const, иными
словами, *this имеет спецификацию const.
   Функция-член const (т.е. функция-член, описанная со спецификацией
const) может вызываться как для объектов const, так и для объектов
без спецификации const, тогда как функция-член без спецификации
const может вызываться только для объектов без спецификации const,
например:

     void k(s& x, const s& y)
     {
       x.f();
       x.g();
       y.f();
       y.g();   // ошибка
     }

Здесь вызов y.g() является ошибкой, т.к. y есть const, а s::g() -
функция-член без спецификации const, которая может изменять
(и изменяет) объекты, для которых она вызывалась.
   Аналогично, только функция-член volatile (т.е. функция-член,
описанная со спецификацией volatile) может вызываться для объектов
со спецификацией volatile. Функция-член может быть одновременно
const и volatile.
   Для объектов const или volatile могут вызываться конструкторы
($$R.12.1) и деструкторы ($$R.12.4). Конструкторы ($$R.12.1) и
деструкторы ($$R.12.4) нельзя описывать со спецификациями const
или volatile.

R.9.3.2 Функции-члены со спецификацией inline


Функцию-член можно определить ($$R.8.3) в описании класса, в
таком случае она считается подстановкой (inline, $$R.7.1.2).
Определять функцию в описании класса - это эквивалентно тому,
чтобы описывать функцию и определять ее со спецификацией inline
сразу же после описания класса. Считается, что такой перенос
определения функции происходит после препроцессорной обработки
до стадии синтаксического анализа и контроля типов. Поэтому
программный фрагмент

      int b;
      struct x {
         char* f() { return b; }
         char* b;
      };

эквивалентен

      int b;
      struct x {
         char* f();
         char* b;
      };

      inline char* x::f() { return b; } // перенос

Здесь в функции x::f() используется x::b, а не глобальное b.
   Функции-члены можно определять даже в описании локальных или
вложенных классов, где такой перенос будет синтаксически незаконным.
Локальные классы обсуждаются в R.9.8, а вложенные классы в $$R.9.7.

R.9.4 Статические члены


Для члена класса, представляющего данные или функцию, можно при описании
класса задать спецификацию static. Для статического члена,
представляющего данные, в программе существует только один экземпляр,
которым владеют все объекты этого класса. Статический член не является
частью объекта класса. Статические члены глобального класса
подлежат внешнему связыванию ($$R.3.3). Описание статического члена,
представляющего данные, в описании класса не считается определением.
Определение должно быть дано в другом месте, см. также. $$R.18.3.
    Статическая функция-член не имеет указатель this, поэтому для
доступа к нестатическим членам своего класса она должна использовать
операции . или ->. Статическая функция-член не может быть виртуальной.
Недопустимы статические и нестатические функции-члены с одним именем
и одинаковыми типами параметров.
    Статические члены локального класса ($$R.9.8) не подлежат
связыванию и не могут определяться вне описания класса. Отсюда
следует, что локальные классы не могут иметь статических членов,
представляющих данные.
    К статическому члену mem класса c1 можно обращаться как c1::mem
($$R.5.1), т.е. независимо ни от какого объекта. К нему также можно
обращаться с помощью операций доступа к членам . и  ->. Если к
статическому члену происходит обращение с помощью операций доступа,
выражения, стоящие слева от . или -> не эквивалентны. Статический
член mem существует даже, если не создано ни одного объекта класса
c1. В примере ниже run_chain, idle и другие члены существуют даже,
если не было создано ни одного объекта класса process:

        class process {
          static int no_of_process;
          static process* run_chain;
          static process* running;
          static process* idle;
          // ...
        public:
          // ...
          int state();
          static void reshedule();
          // ...
        };

Здесь к функции reshedule можно обратиться без указания объекта
класса process таким образом:

        void f()
        {
          process::reshedule();
        }

  Статические члены глобальных классов инициализируются точно так же
как глобальные объекты, но область их видимости - файл, например:

        void process::reshedule() { /* ... */ };
        int process::no_of_process = 1;
        process* process::running = get_main();
        process* process::run_chain = process::running;

Статические члены подчиняются обычным правилам доступа к членам
класса ($$R.11), за исключением того, что их можно инициализировать
в файловой области видимости.
    В типе статического члена не участвует имя класса, так тип
process::no_of_process есть int, а тип &process::reshedule() -
void(*)().


R.9.5 Объединения


Объединение можно представить как структуру, все члены имеют
нулевое смещения, а размер ее достаточно велик, чтобы
вмещать любой из ее членов. В любой момент времени объединение
может содержать только один член. В объединении могут быть
функции-члены (в том числе конструкторы и деструкторы), но не
виртуальные функции ($$R.10.2). Объединение не может иметь базовых
классов и не может само использоваться в качестве базового класса.
Членом объединения не может быть объект класса с конструктором или
деструктором, а также с определенной пользователем операцией
присваивания ($$R.13.4.3). Объединение не может содержать статических
членов, представляющих данные.
    Объединение вида

        union { список-членов }

называется безымянным объединением, оно определяет объект без имени
(и без типа). Имена всех членов безымянного объединения должны
отличаться от других имен в той области видимости, в которой описано
объединение; их можно использовать в этой области видимости
непосредственно, без обычных операций доступа к членам ($$R.5.2.4).
Приведем пример:

       void f()
       {
         union { int a; char* p; };
         a = 1;
         // ...
         p = "Jennifer";
         // ...
        }

Здесь a и p используются как обычные переменные (не члены), но поскольку
они входят в одно объединение, их адреса совпадают.
   Глобальные безымянные объединения можно описать со спецификацией
static. Безымянные объединения не должны содержать частных или
защищенных членов ($$R.11), а также функций-членов.
   Если описаны объекты объединения или указатели на него, то
оно не считается безымянным, например,

        union { int aa; char* p; } obj, *ptr=&obj;
        aa = 1;      // ошибка
        ptr->aa = 1; // нормально

Здесь присваивание простому имени aa незаконно, т.к. имя члена не
привязано ни к какому объекту.
   Инициализация объединений, не имеющих конструкторов, описывается
в $$R.8.4.1.

R.9.6 Битовые поля


Конструкция описатель-члена, имеющая вид,
     идентификатор opt : выражение-константа
задает битовое поле, длина которого отделяется от его имени
двоеточием. Размещение битовых полей в объекте класса зависит от
реализации. Поля упаковываются в некоторые адресуемые элементы
памяти. На одних машинах поля могут выходить за границы этих
элементов, на других - нет. Выравнивание битовых полей тоже определяется
реализацией. На одних машинах значения помещаются в битовые поля
справа налево, на других - слева направо.
   Чтобы установить заданное расположение полей с помощью дополнения
нулями, используют безымянные битовые поля. Особый случай, когда
используется безымянное поле нулевой длины. Оно задает выравнивание
следующего битового поля по границе элемента памяти, используемого
при размещении полей.
   Безымянное поле не является членом и не может инициализироваться.
   Битовые поля должны иметь целочисленный тип ($$R.3.6.1). Их
интерпретация зависит от того, считается ли значение поля с обычным типом
int (т.е. без явного использования signed или unsigned) знаковым
или беззнаковым. Операция взятия адреса & не применима к битовым
полям, так что не может быть ни указателей на битовые поля, ни ссылок
на них.

R.9.7 Вложенные описания классов


Класс можно описать в описании другого класса. Такой класс называют
вложенным. Имя вложенного класса локально по отношению к
объемлющему классу. Вложенный класс находится в области видимости
объемлющего класса. Если не считать явного использования указателей,
ссылок или имен объектов, то в описаниях вложенного класса допустимы
только имена типов, статических членов и элементов перечисления
из объемлющего класса.

        int x;
        int y;

        class enclose {
        public:
         int x;
         static int s;

         class inner {

            void f(int i)
            {
              x = i;   // ошибка: присваивание enclose::x
              s = i;   // нормально: присваивание enclose ::s
              ::x = i; // нормально: присваивание глобальному x
              y = i;   // нормально: присваивание глобальному y
            }

            void g(enclose* p, int i)
            {
              p->x = i; // нормально: присваивание enclose ::x
            }

          };
        };
        inner* p = 0;   // ошибка: `inner' вне области видимости

Функции-члены вложенного класса не имеют особых прав доступа к членам
объемлющего класса, они подчиняются обычным правилам доступа ($$R.11).
Аналогично, функции-члены объемлющего класса не имеют особых прав
доступа к членам вложенного класса и подчиняются обычным правилам
доступа, например:

         class E {
            int x;
            class I {
               int y;
               void f(E* p, int i)
               {
                 p->x = i;  // ошибка: E::x частный член
               }
             };

             int g(I* p)
             {
               return p->y;  // ошибка: I::y частный член
             }
           };

Функции-члены и представляющие данные, статические члены из вложенного
класса можно определить в глобальной области видимости, например:

           class enclose {
              class inner {
                 static int x;
                 void f(int i);
              };
            };

            typedef enclose::inner ei;
            int ei::x = 1;

            void enclose::inner::f(int i) { /* ... */ }

Подобно функции-члену дружественная функция, определенная в данном
классе, находится в области видимости этого класса. Она подчиняется
тем же правилам связывания имен, что и функции-члены (они указаны выше
и в $$R.10.4), и не имеет так же как они особых прав доступа к
членам объемлющего класса и к локальным переменным функций этого
класса ($$R.11).

R.9.8 Описания локальных классов


Класс можно описать в определении функции, такой класс называется
локальным. Имя локального класса считается локальным в объемлющей
области видимости, а областью видимости локального класса является
объемлющая область видимости. В описаниях локального класса из
объемлющей области видимости можно использовать только имена типов,
статических переменных, внешних переменных и функций, а также
элементы перечисления. Приведем пример:

         int x;
         void f()
         {
           static int s;
           int x;
           extern int g();
           struct local {
              int h() { return x; }   // ошибка: `x' автоматическая
              int j() { return s; }   // нормально
              int k() { return ::x; } // нормально
              int l() { return g(); } // нормально
           }
         }

   Объемлющая функция не имеет особых прав доступа к членам локального
класса, она подчиняется обычным правилам доступа ($$R.11).
Функцию-член локального класса следует определять в определении этого
класса. Локальный класс не может иметь статических членов,
представляющих данные.

R.9.9 Имена локальных типов


Имена типов подчиняются точно таким же правилам областей видимости,
как и другие имена. В частности, имена типов, определенные в описании
класса, нельзя использовать вне этого класса без уточнения, например:

        class X {
        public:
          typedef int I;
          class Y { /* ... */ }
          I a;
        };

        I b;    // ошибка
        Y c;    // ошибка
        X::Y d; // ошибка

Следующее положение ограничивает зависимость от контекста правил
описания членов класса, а так же правила переноса тела функций,
являющихся подстановками. После использования в описании класса
имя константы, имя-класса или имя-typedef не может переопределяться в
описании этого класса. Имя, не являющееся именем-класса или именем-typedef
не может быть определено в описании класса как имя-класса или имя-typedef,
если оно уже использовалось иначе в описании этого класса.
Рассмотрим пример:

        typedef int c;
        enum { i = 1 };
        class X {
          char v[i];
          int f() { return sizeof(c); }
          char c;          // ошибка: имя typedef
                           // переопределяется после использования
          enum { i = 2 };  // ошибка: `i' переопределяется после
                           // использования в задании типа `char[i]'
        };

       typedef char* T;

       struct Y {
         T a;
         typedef long T;  // ошибка: имя T уже использовано
         T b;
       };

R.10 Производные классы


В описании класса можно указать список базовых классов с помощью
следующих конструкций:

       спец-базовых:
           : список-базовых

       список-базовых:
           спецификация-базовых
           список-базовых , спецификация-базовых

       спецификация-базовых:
           полное-имя-класса
           virtual спецификация-доступа opt полное-имя-класса
           спецификация-доступа virtual opt полное-имя-класса

       спецификация-доступа:
           private
           protected
           public

Конструкция имя-класса в спецификации-базовых должна обозначать
ранее описанный класс ($$R.9), который называется базовым по
отношению к определяемому классу. Говорят, что класс является
производным от своих базовых классов. Назначение конструкции
спецификация-доступа объясняется в $$R.11. К членам базового класса,
если только они не переопределены в производном классе, можно обращаться
так, как будто они являются членами производного класса. Говорят,
что производный класс наследует члены базового класса. С помощью
операции разрешения области видимости :: ($$R.5.1) к члену базового
класса можно обращаться явно. Такое обращение возможно и в том случае,
когда имя члена базового класса переопределено в производном классе.
Производный класс сам может выступать как базовый при контроле
доступа, см. $$R.11.2. Указатель на производный класс может неявно
преобразовываться в указатель на однозначно определенный и доступный
базовый класс ($$R.4.6). Ссылка на производный класс может неявно
преобразовываться в ссылку на однозначно определенный и доступный
базовый класс ($$R.4.7).
   Рассмотрим пример:

        class base {
        public:
           int a, b;
        };

       class derived : public base {
       public:
          int b, c;
       };

       void f()
       {
         derived d;
         d.a = 1;
         d.base::b = 2;
         d.b = 3;
         d.c = 4;
        base* bp = &d;  // стандартное преобразование derived* в base*
       }

Здесь присваиваются значения четырем членам d, а bp настраивается
на d.
   Класс называется прямым базовым, если он находится в списке-базовых,
и косвенным базовым, если сам не являясь прямым базовым, он служит
базовым для одного из классов списка-базовых.
   Отметим, что в обозначении имя-класса :: имя конструкция, имя может
быть именем члена косвенного базового класса. Такое обозначение
просто указывает класс, в котором следует начинать поиск этого имени.
Приведем пример:

       class A { public: void f(); }
       class B : public A { };
       class C : public B { public: void f(); }

       void C::f()
       {
         f();    // вызов f() из C
         A::f(); // вызов f() из A
         B::f(); // вызов f() из A
       }

Здесь дважды вызывается A::f(), поскольку это единственная функция f()
в классе B.
    Инициализация объектов, представляющих базовые классы, задается
в конструкторах, см. $$R.12.6.2.

R.10.1 Множественные базовые классы


Класс может быть производным по отношению к любому числу базовых
классов. Приведем пример:

       class A { /* ... */ };
       class B { /* ... */ };
       class C { /* ... */ };
       class D : public A, public B, public C { /* ... */ };

Использование более, чем одного прямого базового класса называется
множественным наследованием.
   Порядок наследования не важен, если не учитывать вопросов,
связанных со стандартной инициализацией с помощью конструктора
($$R.12.1), уничтожением ($$R.12.4) и размещением в памяти
($$r.5.4, $$R.9.2, $$R.11.1). Порядок выделения памяти для базовых
классов определяется реализацией.
   Нельзя указывать класс в качестве прямого базового по отношению
к производному классу более одного раза, но косвенным базовым классом
он может быть неоднократно.

       class B { /* ... */ };
       class D : public B, public B { /* ... */ }; // недопустимо

       class L { /* ... */ };
       class A : public L { /* ... */ };
       class B : public L { /* ... */ };
       class C : public A, public B { /* ... */ }; // нормально

Здесь объект класса C будет иметь два вложенных объекта класса L.
   К спецификации базового класса можно добавить служебное слово
virtual. Отдельный объект виртуального базового класса V разделяется
между всеми базовыми классами, которые указали V при задании своих
базовых классов. Приведем пример:

       class V { /* ... */ };
       class A : virtual public V { /* ... */ };
       class B : virtual public V { /* ... */ };
       class C : public A, public B { /* ... */ };

Здесь объект класса C будет иметь только один вложенный объект
класса V.
   Класс может содержать виртуальные и невиртуальные базовые классы одного
типа, например:

       class B { /* ... */ };
       class X : virtual public B { /* ... */ };
       class Y : virtual public B { /* ... */ };
       class Z : public B { /* ... */ };
       class AA : public X, public Y, public Z { /* ... */ };

Здесь объект класса AA будет иметь два вложенных объекта класса B:
из класса Z  и виртуальный, разделяемый между классами X и Y.

R.10.1.1 Неоднозначности


Доступ к базовому классу должен быть задан однозначно. Доступ к
члену базового класса считается неоднозначным, если выражение,
используемое для доступа, задает более одной функции, объекта,
типа или элемента перечисления. Проверка на однозначность происходит
до проверки возможности доступа ($$R.11). Приведем пример:

       class A {
       public:
         int a;
         int (*b)();
         int f();
         int f(int);
         int g();
       };

       class B {
          int a;
          int b();
       public:
          int f();
          int g();
          int h();
          int h(int);
       };

       class C : public A, public B { };

       void g(C* pc)
       {
         pc->a = 1;    // ошибка: неоднозначность: A::a или B::a
         pc->b();      // ошибка: неоднозначность: A::b или B::b
         pc->f();      // ошибка: неоднозначность: A::f или B::f
         pc->f(1);     // ошибка: неоднозначность: A::f или B::f
         pc->g();      // ошибка: неоднозначность: A::g или B::g
         pc->g = 1;    // ошибка: неоднозначность: A::g или B::g
         pc->h();      // нормально
         pc->h(1);     // нормально
       }

Если имя перегруженной функции установлено однозначно, то прежде
проверки возможности доступа происходит еще и разрешение перегрузки.
Неоднозначность можно устранить, уточняя используемое имя именем
класса, например, так:

       class A {
       public:
         int f();
       };

       class B {
       public:
         int f();
       };

       class C : public A, public B {
         int f() { return A::f() + B::f(); }
       };

Если используются виртуальные базовые классы, до отдельной функции,
объекта, типа или элемента перечисления можно добраться несколькими
путями, двигаясь по направленному ацикличному графу, который
образуют базовые классы. Но это не является неоднозначностью.
Идентичное же использование невиртуальных базовых классов
порождает неоднозначность, поскольку в этом случае участвует в
задании доступа более одного вложенного объекта. Приведем пример:

       class V { public: int v; };
       class A { public: int a; };
       class B : public A, public virtual V { };
       class C : public A, public virtual V { };

       class D : public B, public C { public: void f(); };

       void D::f()
       {
         v++;   // нормально
         a++;   // ошибка, неоднозначность:  `a' в `D' входит дважды
       }

Если используются виртуальные базовые классы, возможно что
двигаясь по направленному ацикличному графу, можно добраться более,
чем до одного имени функции, объекта или элемента перечисления. Это,
конечно, неоднозначность, но кроме случая, когда одно имя доминирует
над другими. Идентичное использование невиртуальных базовых классов
всегда приводит к неоднозначности, т.к. в этом случае всегда участвует
более одного вложенного объекта.
     Считается, что имя B::f доминирует над именем A::f, если класс
A является для класса B базовым. Если одно имя доминирует над
другим, они не могут привести к неоднозначности: в ситуации выбора
используется всегда доминирующее имя. Приведем пример:

       class V { public: int f(); int x; };
       class B : public virtual V { public: int f(); int x; };
       class C : public virtual V { };

       class D : public B, public C { void g(); };

       void D::g()
       {
         x++;   // нормально: B::x доминирует над V::x
         f();   // нормально: B::f() доминирует над V::f()
       }

В результате явного или неявного преобразования указателя или ссылки
на производный класс в указатель или ссылку на один из его базовых
классов, эти указатель или ссылка должны указывать только на тот
же самый объект, который представляет базовый класс. Приведем пример:

       class V { };
       class A { };
       class B : public A, public virtual V { };
       class C : public A, public virtual V { };
       class D : public B, public C { };

       void g()
       {
         D d;
         B* pb = &d;
         A* pa = &d;  // ошибка, неоднозначность: A из C или A из B?
         v* pv = &d;  // нормально: только один вложенный объект V
       }


R.10.2 Виртуальные функции


Если класс base содержит виртуальную ($$R.7.1.2) функцию vf, а
производный от него класс derived также содержит функцию vf того
же типа, тогда вызов vf для объекта класса derived является
обращением к derived::vf, даже если доступ к этой функции происходит
через указатель или ссылку на класс base. Говорят, что функция
производного класса подавляет функцию базового класса. Однако, если
типы функций ($$R.8.2.5) различны, функции считаются разными и механизм
виртуальности не действует (см. также $$R.13.1). Считается ошибкой,
если функция производного класса отличается от виртуальной функции
базового класса только типом возвращаемого значения. Рассмотрим
пример:

        struct base {
          virtual void vf1();
          virtual void vf2();
          virtual void vf3();
          void f();
        };

        class derived : public base {
        public:
          void vf1();
          void vf2(int);     // скрывает base::vf2()
          char vf3();        // ошибка: различие только в типе
                             // возвращаемого значения
        }

        void g()
        {
          derived d;
          base* bp = &d;     // стандартное преобразование: derived* в base*
          bp->vf1();         // вызов derived::vf1
          bp->vf2();         // вызов base::vf2
          bp->f();           // вызов base::f
        }

Здесь три вызова для объекта d класса derived приведут к обращениям к
derived::vf1, base::vf2 и base::f соответственно. Иными словами,
интерпретация вызова виртуальной функции зависит от типа объекта,
для которого она вызывается, тогда как интерпретация вызова
невиртуальной функции-члена зависит только от типа указателя или
ссылки на этот объект. Например, выражение bp->vf1()
приведет к вызову derived::vf1(), поскольку bp указывает на объект
класса derived, в котором функция derived::vf1() подавляет
виртуальную функцию base::vf1().
   Наличие спецификации virtual означает, что функция является членом,
поэтому виртуальная функция не может быть глобальной функцией (не членом)
($$R.7.1.2). Точно так же виртуальная функция не может быть
статическим членом, т.к. для вызова виртуальной функции необходимо
наличие определенного объекта, который указывает, какую функцию
надо вызывать. В другом классе виртуальную функцию можно описать как
друга. Функция, подавляющая виртуальную, сама считается виртуальной
функцией. Спецификацию virtual можно использовать для подавляющей
функции производного класса, но это избыточно. Виртуальная функция
может быть определена или описана в базовом классе как чистая
($$R.10.3). Виртуальную функцию, которая определена в базовом классе,
не нужно определять в производном классе: при всех вызовах будет
использоваться функция, определенная в базовом классе.
    Механизм виртуальности при вызове отключается, если есть явное
уточнение имени с помощью оператора разрешения области видимости
 ($$R.5.1), например:

       class B { public: virtual void f(); };
       class D : public B { public: void f(); };

       void D::f() { /* ... */ B::f(); }

Здесь обращение к f из D приводит к вызову B::f, а не D::f.

R.10.3 Абстрактные классы


Абстрактные классы дают средство для представления в языке общих
понятий, таких, например, как фигура, для которых могут использоваться
только конкретные их варианты, например, круг или квадрат. Кроме того
абстрактный класс позволяет задать интерфейс, разнообразные реализации
которого представляют производные классы.
   Абстрактным называется класс, который можно использовать только
как базовый для некоторого другого класса, т.е. нельзя создать
никакого объекта абстрактного класса кроме того, который представляет
базовый класс для некоторого производного класса. Класс считается
абстрактным, если в нем есть хотя бы одна чистая виртуальная
функция. При описании класса виртуальная функция описывается как
чистая с помощью спецификации-чистой ($$R.9.2). Чистую виртуальную
функцию не нужно определять, если только она явно не вызывается
с помощью конструкции уточненное-имя ($$R.5.1). Рассмотрим пример:

         class point { /* ... */ };
         class shape {     // абстрактный класс
           point center;
           // ...
         public:
           point where() { return center; }
           void move(point p) { center=p; draw(); }
           virtual void rotate(int) = 0; // чистая виртуальная
           virtual void draw() = 0;      // чистая виртуальная
           // ...
         };

Абстрактный класс нельзя использовать как тип формального параметра,
тип возвращаемого значения, а также как тип в операции явного
преобразования типа. Можно описывать указатели и ссылки на абстрактный
класс, например:

         shape    x;       // ошибка: объект абстрактного класса
         shape*   p;       // нормально
         shape    f();     // ошибка
         void g(shape);    // ошибка
         shape& h(shape&); // нормально

  Чистые виртуальные функции и наследуются как чистые виртуальные
функции, например:

         class ab_circle : public shape {
           int radius;
         public:
           void rotate(int) { }
           // ab_circle::draw() чистая виртуальная функция
         };

Поскольку функция shape::draw() является чистой виртуальной функцией,
то такой же будет по определению и функция ab_circle::draw(). Для
приведенного ниже описания класс circle не будет абстрактным, и у
функции circle::draw() где-то должно существовать определение.

         class circle : public shape {
            int radius:
         public:
            void rotate(int) { }
            void draw();  // должна быть где-то определена
         };

   Функции-члены можно вызывать из конструктора абстрактного класса,
результат прямого или косвенного вызова чистой виртуальной функции
для объекта, созданного с помощью такого конструктора, неопределен.

R.10.4 Сводка правил области видимости


Теперь можно свести воедино правила областей видимости для программы
на С++. Эти правила одинаково применимы для всех имен (включая
имя-typedef ($$R.7.1.3) и имя-класса ($$R.9.1)) и в любом
контексте, для которого они допустимы по синтаксису языка. Здесь
рассматриваются только области видимости на лексическом уровне,
вопросы связывания обсуждаются в $$R.3.3. Понятие момента описания
было введено в $$R.3.2.
    Всякое использование имени должно быть однозначным (не считая
перегрузки) в области его видимости ($$R.10.1.1). Правила доступа
($$R.11) начинают действовать только тогда, когда имя можно однозначно
найти в области его видимости. Только при условии, что права доступа
к имени не нарушены, начинается проверка типа объекта, функции или
элемента перечисления.
   Имя, которое используется вне любой функции или класса, или перед
которым стоит унарная операция разрешения области видимости ::
(и которое не уточняется бинарной операцией :: или операциями ->
или .), должно быть именем глобального объекта, или функции, или
элемента перечисления, или типа.
   Имя, задаваемое после X:: или obj., где obj типа X или типа
ссылка на X, а также имя, задаваемое после ptr->, где ptr типа указатель
на X, должно быть именем члена класса X или членом базового по
отношению к X класса. Помимо этого, в обращении ptr->имя  ptr может
быть объектом класса Y, в котором есть функция operator->(),
описанная таким образом, что ptr->operator() в конечном счете
оказывается указателем на X ($$R.13.4.6).
  Имя, которое не уточняется одним из описанных выше способов, и,
которое используется в функции, не являющейся членом класса,
должно быть описано в том блоке, где оно используется, или в
объемлющем блоке или должно быть глобальным. Описание локального имени
скрывает описания того же имени в объемлющих блоках, а также его
описания как глобального имени. В частности, перегрузка имени
невозможна для имен в разных областях видимости ($$R.13.4).
  Имя, которое не уточняется одним из описанных выше способов, и,
которое используется в функции, являющейся нестатическим членом
класса X, должно быть описано или в том блоке, где оно используется,
или в объемлющем блоке, и оно должно быть членом класса X, или
членом базового по отношению к X класса, или это имя должно быть
глобальным. Описание локальных имен скрывает описание этих же имен
в объемлющих блоках, в членах класса этой функции и среди глобальных
имен. Описание члена скрывает аналогичные описание с тем же именем
в базовых классах и среди глобальных имен.
   Имя, которое не уточняется одним из описанных выше способов, и,
которое используется в статической функции-члене класса X, должно
быть описано или в том блоке, где оно используется, или в объемлющем
блоке, и должно быть статическим членом класса X, или базового по
отношению к X класса, или оно должно быть глобальным именем.
   Имя формального параметра функции, заданное при ее определении
($$R.8.3), принадлежит области видимости, совпадающей с наибольшим
блоком функции (в частности, является локальным именем). Имя
формального параметра функции, заданное в ее описании ($$R.8.2.5),
а не определении, принадлежит локальной области видимости, которая
исчезает сразу же после описания функции. Стандартные значения
параметров находятся в области видимости, определяемой в момент
описания ($$R.3.2) формальных параметров функции; в них не должны
использоваться локальные переменные или нестатические члены класса,
и они вычисляются при каждом вызове функции ($$R.8.2.6).
   Инициализатор-ctor ($$R.12.6.2) вычисляется в области видимости
наибольшего блока конструктора, для которого он задан. В частности,
в нем можно использовать имена формальных параметров.

R.11 Контроль доступа к членам


Член класса может быть:
    частным (private); это значит, что его имя можно использовать
    только в функциях-членах и друзьях класса, в котором он описан;

    защищенным (protected); это значит, что его имя можно использовать
    только в функциях-членах и друзьях класса, в котором он описан,
    а также в функциях-членах и друзьях классов, являющихся
    производными по отношению к этому классу (см. $$R.11.5);

    общим (public); это значит, что его имя можно использовать
    в любой функции.
   Члены класса, описанного со служебным словом class, являются
частными по определению. Члены класса, описанного со служебным
словом struct или union, являются общими по определению, например:

      class X {
         int ;    // X:: частный по определению
      };

      struct S {
         int a;   // S::a общий по определению
      };

R.11.1 Спецификации доступа


Описания членов могут быть снабжены спецификацией доступа ($$R.10):
      спецификация-доступа : список-членов opt
Спецификация-доступа задает правила доступа к членам, которые
действуют до конца жизни класса или пока не появится другая
спецификация-доступа, например,

      class X {
        int a;  // X::a частный по определению: учитывается 'class'
      public:
        int b;  // X::b общий
        int c;  // X::c общий
      };

Допустимо любое число спецификаций доступа и задавать их можно в
любом порядке, например,

     struct S {
        int a;  // S::a общий по определению: учитывается `struct'
     protected:
        int b;  // S::b защищенный
     private:
        int c;  // S::c частный
     public:
        int d;  // S:: d общий
     };

  Порядок размещения членов, представляющих данные, которые имеют
разные спецификации-доступа, определяется реализацией ($$R.9.2).

R.11.2 Спецификации доступа для базовых классов


Если класс описан как базовый ($$r.10) по отношению к
другому классу с помощью спецификации доступа public, то члены со
спецификацией public или protected из базового класса являются
соответственно членами с той же спецификацией для производного класса.
Если класс описан как базовый по отношению к другому с помощью
спецификации доступа private, то члены со спецификацией public или
protected из базового класса являются членами со спецификацией
private для производного класса. Частные члены базового класса
остаются недоступными даже для производных классов, если только для
обеспечения доступа при описании базового класса не было использовано
описание friend.
    Если для базового класса не указана спецификация-доступа, то
для производного класса, если он описан как struct, предполагается
спецификация public, а если он описан со служебным словом
class, то - спецификация private, например:

       class B { /* ... */ };
       class D1 : private B { /* ... */ };
       class D2 : public B { /* ... */ };
       class D3 : B { /* ... */ }; // `B' частный по определению
       struct D4 : public B { /* ... */ };
       struct D5 : private B { /* ... */ };
       struct D6 : B { /* ... */ }; // `B' частный по определению

Здесь класс является общим (public) базовым классом для D2, D4 и D6
и частным (private) базовым классом для D1, D2 и D5.
    Описание базового класса как private не влияет на доступ к
статическим членам базового класса. Однако, если при обращении к
статическому члену используется объект или указатель, который
нужно преобразовывать, то действуют обычные правила преобразования
указателей.
    В функциях-членах или друзьях класса X можно X* неявно
преобразовывать в указатель на частный класс, являющийся
непосредственно базовым по отношению к X.


R.11.3 Описания доступа


Используя уточненное имя, можно установить доступ к члену базового
класса в части public или protected описания производного класса.
Это называется описанием доступа.
   Приведем пример:

       class B {
         int a;
       public:
         int b, c;
         int bf();
       };

       class D : private B {
         int d;
       public:
         B::c;   // adjust access to `B::c'
         int e;
         int df();
       };

       int ef(D&);

Во внешней функции ef можно использовать только имена c, e, и df.
Поскольку функция df член класса D, в ней можно использовать
имена b, c, bf, d, e и df, но не a. Функция bf - член класса B и
в ней можно использовать члены a, b, c и bf.
   Описания доступа не следует использовать для ограничения доступа
к члену, доступному в базовом классе, также как не следует
использовать его для обеспечения доступа к члену, который недоступен
в базовом классе, например:

       class B {
       public:
         int a;
       private:
         int b;
       protected:
         int c;
       };

       class D : private B {
       public:
         B::a;    // описать `a' как общий член D
         B::b;    // ошибка: попытка расширить доступ,
                  // `b' не может быть общим членом D
       protected:
         B::c;    // описать `c' как защищенный член D
         B::a;    // ошибка: попытка сузить доступ,
                  // `a' не может быть защищенным членом D
       };

Описание доступа для имени перегруженной функции устанавливает
доступ в базовом классе ко всем функциям с этим именем, например:

       class X {
       public:
         f();
         f(int);
       };

       class Y : private X {
       public:
         X::f;   // makes X::f() and X::f(int) public in Y
       };

   Нельзя в производном классе установить доступ к члену базового
класса, если в производном классе определен член с этим же именем,
например:

       class X {
       public:
         void f();
       };

       class Y : private X {
       public:
         void f(int);
         X::f;   // ошибка: два описания f
       };

R.11.4 Друзья


Другом класса называется функция, которая не является членом класса,
но в которой можно использовать частные и защищенные члены этого
класса. Имя друга не принадлежит области видимости класса, и
дружественная функция не вызывается с помощью операций доступа к
членам ($$R.5.2.4), если только она не является членом другого
класса. Следующий пример показывает различие между членами и
друзьями:

       class X {
         int a;
         friend void friend_set(X*, int);
       public:
         void member_set(int);
       };

       void friend_set(X* p, int i) { p->a = i; }
       void X::member_set(int i) { a = i; }

       void f()
       {
         X obj;
         friend_set(&obj,10);
         obj.member_set(10);
       }

   Если в описании friend использовано имя перегруженной функции
или операции, только функция, однозначно определяемая типами
формальных параметров, становится другом. Функция-член класса X
может быть другом класса Y, например:

       class Y {
         friend char* X::foo(int);
         // ...
       };

Можно объявить все функции класса X друзьями класса Y с помощью
спецификации-сложного-типа ($$R.9.1):

       class Y {
         friend class X;
         // ...
       };

Описание одного класса как друг другого класса дополнительно
подразумевает, что частные и защищенные члены класса, предлагающего
дружбу, могут использоваться в классе, получающем ее, например:

       class X {
         enum { a=100 };
         friend class Y;
       };

       class Y {
         int v[X::a];   // Y друг класса X
       };

       class Z {
         int v[X::a];   // ошибка: X::a недоступно
       };

  Если класс или функция, объявленные как друзья, не были описаны,
их имена попадают в ту же область видимости, что и имя класса,
содержащего описание friend ($$R.9.1).
  Функция, появившаяся первый раз в описании friend, считается
эквивалентной функции, описанной как extern ($$R.3.3, $$r.7.1.1).
  Если функция-друг определена в описании класса, она считается
функцией со спецификацией inline и к ней применимо правило
переноса определения функции для функций-членов ($$R.9.3.2).
Функция-друг, определенная в описании класса, относится на
лексическом уровне к области видимости этого класса. Для
функции-друга, определенной вне класса, это не так.
  На описание friend не влияет указание спецификаций-доступа
($$R.9.2).
Понятие дружбы не является ни наследуемым, ни транзитивным.
Подтвердим это примером:

     class A {
       friend class B;
       int a;
     };

     class B {
       friend class C;
     };

     class C {
       void f(A* p);
       {
         p->a++;   // ошибка: C не друг класса A, хотя
                   // является другом друга класса A
       }
     };

     class D : public B {
       void f(A* p)
       {
         p->a++;   // ошибка: D не друг класса A, хотя
                   // является производным друга класса A
       }
     };

R.11.5 Доступ к защищенным членам


Друг или функция-член производного класса имеет доступ к защищенному
статическому члену базового класса. Друг или функция-член производного
класса могут получить доступ к защищенному нестатическому члену
одного из своих базовых классов только через указатель, ссылку или
объект производного класса (или любого класса, являющегося
производным по отношению к нему). Рассмотрим пример:

     class B {
     protected:
       int i;
     };

    class D1 : public B {
    };

    class D2 : public B {
      friend void fr(B*, D1*, D2*);
      void mem(B*, D1*);
    };

   void fr(B* pb, D1* p1, D2* p2)
   {
     pb->i = 1;  // недопустимо
     p1->i = 2;  // недопустимо
     p2->i = 3;  // нормально (обращение через D2)
   }

   void D2::mem(B* pb, D1* p1)
   {
     pb->i = 1;  // недопустимо
     p1->i = 2;  // недопустимо
     i = 3;      // нормально (обращение через this)
   }

   void g(B* pb, D1* p1, D2* p2)
   {
     pb->i = 1;  // недопустимо
     p1->i = 2;  // недопустимо
     p2->i = 3;  // недопустимо
   }

R.11.6 Доступ к виртуальным функциям


Правила доступа ($$r.11) к виртуальной функции определяются ее
описанием и на них не влияют правила доступа к к функции, которая
позднее будет подавлять ее. Приведем пример:

    class B {
    public:
      virtual f();
    };

    class D : public B {
    private:
      f();
    };

    void f()
    {
      D d;
      B* pb = &d;
      D* pd = &d;

      pb->f();   // нормально: B::f() общий член
                 // вызывается D::f()
      pd->f();   // ошибка: D::f() частный член
    }

Права доступа проверяются при самом вызове, используя тип выражения,
обозначающее объект, для которого вызывается функция-член (в примере
выше это B*). Доступ к функции-члену в классе, где она определена
(D в примере выше), в общем случае неизвестен.

R.11.7 Множественный доступ


Если добраться до имени можно несколькими путями по графу, задающему
множественное наследование, то право доступа этого имени считается
максимальным из прав, получаемых на разных путях. Поясним это
примером:

     class W { public: void f(); };
     class A : private virtual W { };
     class B : public virtual W { };
     class C : public A, public B {
        void f() { W::f(); } // нормально
     };

Поскольку W::f() доступно в C::f() по пути, связанному с общим
наследованием из B, обращение является законным.

R.12 Специальные функции-члены


Некоторые функции-члены считаются специальными, поскольку они
влияют на то, как объекты класса создаются, копируются и уничтожаются,
и как значения одного типа преобразуются в значения другого типа.
Часто такие функции вызываются неявно.
    Эти функции-члены подчиняются обычным правилам доступа ($$R.11).
Например, описание конструктора со спецификацией protected
гарантирует, что создавать объекты с его помощью смогут только
производные классы и друзья.

R.12.1 Конструкторы


Конструктором называется функция-член, имя которой совпадает с именем
класса, он используется для построения значений, имеющих тип данного
класса. Если в классе есть конструктор, то каждый объект этого класса
перед произвольным использованием будет инициализироваться, см.
$$R.12.6.
   Конструктор может вызываться для объекта со спецификацией const
или volatile. Сам конструктор нельзя описывать со спецификацией
const или volatile ($$R.9.3.1). Конструктор также не может иметь
спецификацию virtual или static.
   Конструкторы не наследуются, однако, стандартные конструкторы
и конструкторы копирования при необходимости создаются транслятором
($$R.12.8). Такие конструкторы являются общими.
   Стандартным конструктором для класса X является такой конструктор
класса X, который можно вызывать без параметров. Стандартный
конструктор для класса X будет создан только тогда, когда для класса
X не описано ни одного конструктора.
   Конструктором копирования для класса X называется конструктор,
который вызывается для копирования объекта класса X, т.е. вызывается
с одним параметром типа X. Например, X::X(const X&) и
X::X(X&, int=0) являются конструкторами копирования. Конструктор
копирования создается только тогда, когда не описано ни одного
конструктора копирования.
   Конструктор копирования для класса X не должен иметь в качестве
параметра объект типа X, например X::X(X) незаконное обращение.
   Конструктор для массива элементов вызывается в порядке
возрастания адресов элементов ($$R.8.2.4).
   Если у класса есть базовые классы с конструктором или члены,
являющиеся объектами с конструктором, их конструкторы вызываются
прежде, чем конструктор производного класса. В $$R.12.6.2 объясняется
как задаются параметры для таких конструкторов и как определяется
порядок их вызова.
   Объект класса с конструктором не может быть членом объединения.
   Для конструктора не нужно указывать никакого типа возвращаемого
значения, даже void. В операторе return в теле конструктора нельзя
указывать возвращаемое значение. Не допустима операция взятия
адреса конструктора.
   Конструктор можно явно использовать для создания объектов его
типа с помощью следующей записи:
        имя-класса ( список-выражений opt )
Приведем пример:

        complex zz = complex(1,2.3);
        print( complex(7.8,1.2) );

Объект, созданный таким образом является безымянным (если только
конструктор не использовался для инициализации поименованной переменной
как zz выше), а время его жизни ограничено выражением, в котором
он был создан, см. $$R.12.2.
    В конструкторе можно вызывать функцию-член, см. $$R.12.7.

R.12.2 Временные объекты


В некоторых ситуациях транслятору бывает необходимо или удобно
создавать временные объекты. Использование временных объектов
зависит от реализации. Если транслятору понадобился временный
объект типа класса с конструктором, он должен обеспечить вызов
конструктора для этого временного объекта. Аналогично, необходимо
вызывать деструктор для объекта класса, в котором описан
деструктор. Приведем пример:

        class X {
          // ...
        public:
          // ...
          X(int);
          X(X&);
          ~X();
        };

        X f(X);

        void g()
        {
          X a(1);
          X b = f(X(2));
          a = f(b);
        }

Здесь нужен временный объект для построения X(2), прежде чем
передать его функции f() с помощью X(X&). Альтернативное решение, -
построить объект X(2) в памяти, используемой для хранения параметра
при первом вызове f(). Помимо этого, временный объект может
понадобиться для хранения результата f(X(2)) прежде, чем копировать
его в объект b с помощью X(X&), и здесь возможно альтернативное
решение: хранить результат f(X(2)) в памяти для объекта b.  С другой
стороны, существует много функций f(), для которых выполнение
выражения a=f(a) требует временного объекта или для параметра a,
или для результата f(a), чтобы избежать нежелательного использования
памяти, которой приписывается имя a.
    Транслятор обязан гарантировать уничтожение временных объектов.
Точный момент уничтожения определяется реализацией. С временными
объектами можно производить только две операции: выбрать значение
объекта (неявно копируя его) для использования в другом выражении,
или взять ссылку на него. Если значение временного объекта получено,
он считается ненужным и может уничтожаться немедленно. Если на него
получена ссылка, то уничтожать его нельзя, пока существует ссылка.
Уничтожение должно произойти до выхода из области определенности,
в которой был создан временный объект.
    Другой вид временных объектов обсуждается в $$R.8.4.3.

R.12.3 Преобразования


Преобразования объектов класса можно задать с помощью конструкторов
или функций преобразования.
   Такие преобразования, обычно называемые пользовательскими,
используются неявно в совокупности со стандартными преобразованиями
($$R.4). Например, функцию с формальным параметром типа X можно
вызывать не только с параметром типа X, но и параметром типа T,
если существует преобразование типа T в X. Пользовательские
преобразования применяются в тех же ситуациях, что и стандартные:
преобразование инициализаторов ($$R.8.4), параметров функции
($$R.5.2.2), возвращаемых функцией значений ($$R.6.6.3, $$R.8.2.5),
выражений фактических параметров ($$R.5), выражений, управляющих
циклом и выбором операторов ($$R.6.4,$$R.6.5) и явные операции
преобразования типа ($$R.5.2.3, $$R.5.4).
   Пользовательские преобразования применяются только в случае
их однозначности ($$R.10.1.1, $$R.12.3.2). Преобразования проходят
проверку на соответствие правилам доступа ($$R.11). Как всегда
проверка доступа осуществляется после разрешения неоднозначности
($$R.10.4).
    Применение преобразований при вызове функции рассматривается на
примерах, приведенных ниже, а также обсуждается в $$R.13.2.

R.12.3.1 Преобразование с помощью конструктора


Конструктор, имеющий единственный параметр, задает преобразование
типа своего фактического параметра в тип его класса, например:

         class X {
           // ...
         public:
           X(int);
           X(const char*, int = 0);
         };

         void f(X arg) {
            X a = 1;        // a = X(1);
            X b = "Jessie"; // b = X("Jessie",0)
            a = 2;          // a = X(2)
            f(3);           // f(X(3))
         }

Если в классе X нет конструктора, который допускает заданный тип,
не делается попытки найти какой-либо конструктор другого класса или
функцию преобразования для приведения заданного значения в значение
типа,допустимого для конструктора класса X, например:

         class X { /* ... */ X(int); };
         class Y { /* ... */ Y(X); };
         Y a = 1;        // недопустимо: преобразование Y(X(1))
                         // не применяется

R.12.3.2 Функции преобразования


Функция-член класса X, имя которой имеет вид,

         имя-функции-преобразования:
             operator имя-типа-преобразования

         имя-типа-преобразования:
             список-спецификаций-типа opt операция-ptr opt

задает преобразование из типа X в тип, определяемый конструкцией
имя-типа-преобразования. Такие функции-члены называются функциями
преобразования. В конструкции список-спецификаций-типа нельзя
описывать классы, перечисления и имена-typedef, а также нельзя
задавать типы формальных параметров и тип возвращаемого значения.
   Приведем пример:

          class X {
            // ...
          public:
            operator int();
          };

          void f(X a)
          {
            int i = int(a);
            i = (int)a;
            i = a;
           }

Здесь во всех трех операторах присваиваемое значение будет
преобразовываться с помощью функции X::operator int(). Пользовательские
преобразования не ограничиваются только использованием в присваивании
и инициализации, например:

           void g(X a, X b)
           {
             int i = (a) ? 1+a : 0;
             int j = (a&&b) ? a+b : i;
             if (a) { // ...
             }
           }

   Операции преобразования наследуются. Функции преобразования
могут быть виртуальными.
   К данному значению неявно применяется не более одного
пользовательского преобразования (с помощью конструктора или функции
преобразования), например:

     class X {
       // ...
     public:
       operator int();
     };

     class Y {
       // ...
     public:
       operator X();
     };

     Y a;
     int b = a;     // недопустимо: преобразование
                    // a.operator X().operator int() не применяется
     int c = X(a);  // нормально: a.operator X().operator int()

   Пользовательские преобразования осуществляются неявно только
при условии их однозначности. Функция преобразования производного
класса не закрывает функцию преобразования базового класса, если
только они не преобразуют к одному и тому же типу, например:

           class X {
           public:
             // ...
             operator int();
           };

           class Y : public X {
           public:
             // ...
             operator void*();
           };

           void f(Y& a)
           {
             if (a) {  // ошибка: неоднозначность
             }
           }

R.12.4 Деструкторы


Деструктором называется функция-член класса cl с именем ~cl, она
используется для уничтожения значений типа cl непосредственно перед
уничтожением объекта, содержащего их. Деструктор не имеет формальных
параметров и для него нельзя задать тип возвращаемого значения
(даже void). Нельзя применять операцию взятия адреса для деструктора.
Можно вызывать деструктор для объектов со спецификацией const или
volatile, но сам деструктор нельзя описывать с этими спецификациями
($$R.9.3.1). Деструктор не может быть и статическим.
    Деструкторы не наследуются. Если базовый класс или член имеют
деструктор, а сам производный класс - нет, то создается стандартный
деструктор, который вызывает деструкторы базовых классов и членов
производного класса. Такие созданные деструкторы имеют спецификацию
public.
    Тело деструктора выполняется прежде деструкторов для объектов,
являющихся членами. Деструкторы для нестатических объектов, являющихся
членами, выполняются прежде, чем деструкторы для базовых классов.
Деструкторы для невиртуальных базовых классов выполняются прежде,
чем деструкторы для виртуальных базовых классов. Деструкторы для
невиртуальных базовых классов выполняются в порядке, обратном их
описанию в производном классе. Деструкторы виртуальных базовых
классов выполняются в порядке, обратном появлению их при обходе
снизу и слева-направо ацикличного направленного графа базовых
классов. Здесь "слева-направо" означает порядок появления имен
базовых классов, который был при описании их в производном классе.
   Деструкторы для элементов массива вызываются в порядке,
обратном вызовам при их построении.
   Деструктор может быть виртуальным.
   В деструкторе можно вызывать функцию-член, см. $$R.12.7.
   Объект класса с деструктором не может быть членом объединения.
   Деструкторы вызываются неявно в следующих случаях:
   (1) когда исчезают из области видимости объекты auto ($$R.3.5)
   или временные объекты ($$R.12.2, $$R.8.4.3);
   (2) при завершении программы ($$R.3.4) для построенных статических
   объектов ($$R.3.5);
   (3) благодаря обращению к операции delete ($$R.5.3.4) для объектов,
   созданных с помощью операции new ($$R.5.3.3);
   (4) при явном вызове.
Когда деструктор вызывается операцией delete, то он освобождает
память для самого большего из производных классов ($$R.12.6.2) того
объекта, который использовал операцию delete() ($$R.5.3.4),
например:

           class X {
             // ...
           public:
             X(int);
             ~X();
           };

           void g(X*);

           void f()     // общий случай
           {
             X* p = new X(111);   // размещение и инициализация
             g(p);
             delete p;            // освобождение и удаление
           }

Явные вызовы деструкторов применяются редко. Примером этого может
служить вызов деструктора для объектов, созданных в некоторой
определенной адресной области с помощью операции new. Размещение
объектов в определенном адресном пространстве и последующее
уничтожение их может потребоваться для использования специфических
возможностей аппаратуры и для правильного функционирования оперативной
памяти. Приведем пример:

     void* operator new(size_t, void* p) { return p; }

     void f(X* p);

     static char buf[sizeof(X)];

     void g()     // редкий, специальный случай
     {
       X* p = new(buf) X(222);  // размещение в buf[] и инициализация
       f(p);
       p->X::~X();              // удаление
     }

  Обозначения, использованные для явного вызова деструктора, можно
использовать для имени любого простого типа, например,

          int* p;
          // ...
          p->int::~int();

Использование такой записи для типа, у которого нет деструктора,
проходит бесследно. Допуская такую запись, мы разрешаем пользователям
писать программу, не задумываясь над тем, есть ли данного типа
деструктор.

R.12.5 Свободная память


Когда создается объект с помощью операции new, для получения свободной
памяти вызывается (неявно) функция operator new() ($$R.5.3.3).
    Если функция operator new() не может выполнить запрос, она
возвращает 0.
    В классе X функция X::operator new() является статическим членом,
даже если она не описана явно как static. Первый ее параметр должен
иметь тип size_t, - зависящий от реализации целочисленный тип,
который определен в стандартном заголовочном файле <stddef.h>, и
она должна возвращать значение типа void*, например:

           class X {
             // ...
             void* operator new(size_t);
             void* operator new(size_t, Arena*);
           };

   Правила выбора подходящей функции operator new() обсуждаются
в $$R.5.3.3.
   В классе X функция X::operator delete() является статическим
членом, даже если она не описана явно как static. Первый ее параметр
должен быть типа void* и можно добавлять второй параметр типа
size_t. Она не может возвращать какое-либо значение и тип
возвращаемого значения должен быть void, например:

           class X {
             // ...
             void operator delete(void*);
           };

           class Y {
             // ...
             void operator delete(void*, size_t);
           };

В каждом классе можно описать только одну функцию operator delete(),
значит эта функция не может быть перегруженной. Глобальная функция
operator delete() имеет единственный параметр типа void*.
   Если функция описана с двумя формальными параметрами, она вызывается
с двумя параметрами, второй из которых показывает размер удаляемого
объекта. Передаваемый размер определяется с помощью деструктора
(если он есть) или по типу (статическому) указателя на удаляемый
объект. Операция пройдет корректно, если тип указателя, заданного
как фактический параметр, будет совпадать с типом объекта (а не будет,
к примеру, просто типом указателя на базовый класс) или, если
этот тип является типом указателя на базовый класс с виртуальным
деструктором.
   Для массивов объектов типа класс используются глобальные функции
operator new() и operator delete() ($$R.5.3.3, $$R.5.3.4).
   Поскольку функции X::operator new() и X::operator delete()
статические, они не могут быть виртуальными. Функция
operator delete(), которая вызывается из деструктора для освобождения
памяти, выбирается по обычным правилам областей видимости,
например:

           struct B {
             virtual ~B();
             void* operator new(size_t);
             void operator delete(void*);
           };

           struct D : B {
             ~D();
             void* operator new(size_t);
             void operator delete(void*);
           };

           void f()
           {
             B* p = new D;
             delete p;
           }

В этом примере память для объекта класса D выделяется с помощью
D::operator new(), а благодаря наличию виртуального деструктора,
освобождается с помощью D::operator delete().

R.12.6 Инициализация


Объект класса без конструкторов, без частных или защищенных членов,
без виртуальных функций и без базовых классов можно инициализировать
с помощью списка инициализаторов ($$R.8.4.1). Объект класса с
конструктором должен инициализироваться или иметь стандартный
конструктор ($$R.12.1). Стандартный конструктор используется для
объектов, которые не проходят явной инициализации.

R.12.6.1 Явная инициализация


Объекты классов с конструкторами ($$R.12.1) можно инициализировать
списком выражений, заключенным в скобки. Этот список считается
списком фактических параметров для вызова конструктора, производящего
инициализацию. Иначе, в качестве инициализатора задается с помощью
операции = одно значение. Оно используется как фактический параметр
для конструктора копирования. Обычно можно обойтись без вызова
конструктора копирования, например:

           class complex {
             // ...
           public:
             complex();
             complex(double);
             complex(double,double);
             // ...
           };

           complex sqrt(complex,complex);

           complex a(1);             // инициализация вызовом
                                     // complex(double)
           complex b = a;            // инициализация копированием `a'
           complex c = complex(1,2); // конструктор complex(1,2)
                                     // вызывается complex(double,double)
                                     // и копируется в `c'
           complex d = sqrt(b,c);    // вызывается sqrt(complex,complex),
                                     // результат копируется в `d'
           complex e;                // инициализация вызовом конструктора
           complex f = 3;            // complex(3), вызывается
                                     // complex(double) и результат
                                     // копируется в `f'

Перегрузка операции присваивания = не оказывает влияние на
инициализацию.
   Инициализация, происходящая при передаче фактических параметров
и при возврате из функции, эквивалентна инициализации вида

           T x = a;

Инициализация, происходящая в выражении операции new ($$R.5.3.3) и
при инициализации базовых классов и членов, эквивалентна
инициализации вида

           T x(a);

   Для массивов объектов класса с конструкторами используются при
инициализации ($$R.12.1) конструкторы как и для одиночных объектов.
Если оказалось, что инициализаторов в списке меньше, чем элементов
массива, используется стандартный конструктор ($$R.12.1). Если его
нет, список инициализаторов должен быть полным. Приведем пример:

           complex cc = { 1, 2 }; // ошибка: необходимо
                                  // использовать конструктор
           complex v[6] = { 1,complex(1,2),complex(),2 };

Здесь v[0] и v[3] инициализируются значением complex::complex(double),
v[1] инициализируется complex::complex(double,double), а v[2],
v[4] и v[5] инициализированы complex::complex().
   Объект класса M моет быть членом класса X в одном из следующих
случаев:
  (1) M не имеет конструктора;
  (2) M имеет стандартный конструктор;
  (3) X имеет конструктор и каждый из них задает инициализатор-ctor
      ($$R.12.6.2) для члена M.
В случае 2 при создании составного объекта вызывается стандартный
конструктор. Если член составного объекта имеет деструктор, то он
вызывается при уничтожении составного объекта.
   Конструкторы для нелокальных статических объектов вызываются в
том порядке, в каком они идут в тексте программы, деструкторы
вызываются в обратном порядке, см. также $$R.3.4, $$R.6.7,
$$R.9.4.


R.12.6.2 Инициализация членов и базовых классов


В определении конструктора можно задать инициализацию прямых
базовых классов и членов, не наследуемых из базовых классов. Это
особенно полезно для тех объектов, констант и ссылок, для которых
различаются семантики присваивания и инициализации. Конструкция
инициализатор-ctor имеет вид

           инициализатор-ctor:
                : список-инициализаторов-членов

           список-инициализаторов-членов:
                инициализатор-члена
                инициализатор-члена , список-инициализаторов-члена

           инициализатор-члена:
                полное-имя-класса ( список-выражений opt )
                идентификатор

Список параметров используется для инициализации нестатических членов
или объектов базового класса. Это единственный способ инициализации
нестатических членов, являющихся ссылками или объектами типа const,
например:

          struct B1 { B1(int); /* ... */ };
          struct B2 { B2(int); /* ... */ };

          struct D : B1, B2 {
              D(int);
              B1 b;
              const c;
          };

          D::D(int a) : B2(a+1), B1(a+2), c(a+3), b(a+4)
          { /* ... */ }
          D d(10);

  В начале инициализируются базовые классы в порядке их описания
(независимо от порядка инициализаторов-членов), затем по той же
схеме инициализируются члены, и наконец выполняется тело D::D()
($$R.12.1). Порядок описания выдерживается для того, чтобы гарантировать,
что вложенные объекты и члены будут уничтожаться в порядке, обратном
их инициализации.
   Особый случай представляют виртуальные базовые классы. Они создаются
прежде, чем любой невиртуальный базовый класс и в том же порядке,
в каком появляются при обходе снизу и слева-направо ацикличного
направленного графа базовых классов. Порядок "слева-направо" - это
тот, в котором имена базовых классов задаются при описании в
производном классе.
   Полным называется объект, который не является вложенным объектом,
представляющим некоторый базовый класс. Класс такого объекта
называют наибольшим производным классом объекта. Все вложенные
объекты виртуальных базовых классов инициализируются с помощью
конструктора наибольшего производного класса. Если в конструкторе
наибольшего производного класса не задан инициализатор-члена для
виртуального базового класса, тогда этот виртуальный базовый класс
должен иметь стандартный конструктор,либо не иметь никакого
конструктора. Всякий инициализатор-члена для виртуального базового
класса, заданный не в конструкторе класса полного объекта, игнорируется.
Приведем пример:

           class V {
           public:
             V();
             V(int);
             // ...
           };

           class A : public virtual V {
           public:
             A();
             A(int);
             // ...
           };

           class B : public virtual V {
           public:
             B();
             B(int);
             // ...
           };

           class C : public A, public B, private virtual V {
           public:
             C();
             C(int);
             // ...
           };

           A::A(int i) : V(i) { /* ... */ }
           B::B(int i) { /* ... */ }
           C::C(int i) { /* ... */ }

           V v(1);  // use V(int)
           A a(2);  // use V(int)
           B b(3);  // use V()
           C c(4);  // use V()

   Инициализатор-члена вычисляется в области видимости конструктора,
в котором он появился. Например, в следующем фрагменте

           class X {
             int a;
           public:
             const int& r;
             X()::r(a) { }
           };

X::r инициализируется для каждого объекта класса X ссылкой на X::a.

R.12.7 Конструкторы и деструкторы


В конструкторах и деструкторах можно вызывать функцию-член. Отсюда
следует, что можно вызывать (непосредственно или косвенно)
виртуальные функции. Вызываемая функция должна быть определена
в классе самого конструктора или деструктора или в базовых классах,
но не должна быть функцией, которая их подавляет в производном классе.
Этим обеспечивается то, что еще несозданные объекты не будут
использованы при выполнении конструктора или деструктора. Рассмотрим
пример:

            class X {
            public:
              virtual void f();
              X() { f(); }  // вызов X::f()
              ~X() { f(); } // вызов X::f()
            };

            class Y : public X {
              int& r;
            public:
              void f()
              {
                r++; // беда, если `r' не инициализировано
              }
              Y(int& rr) ::r(rr) { }
            };

    Результат непосредственного или косвенного вызова из конструктора
чистой виртуальной функции для инициализируемого объекта неопределен,
если только явно не использовано уточнение имени функции ($$R.10.3).

R.12.8 Копирование объектов класса


Объекты класса могут копироваться двумя способами: либо присваиванием
($$R.5.17), либо инициализацией ($$R.12.1, $$R.8.4), которая может
происходить при передаче параметров ($$R.5.2.2) или
результата функции ($$R.6.6.3). Для класса X эти две операции
концептуально реализуются как операция присваивания и конструктор
копирования ($$R.12.1). В программе можно определить или одну из них,
или обе. Если пользователь не определил их в программе, то они будут
для всех членов класса X определяться соответственно как присваивание
по членам и инициализация по членам.
    Если все базовые классы и все члены класса X имеют конструктор
копирования, в котором допустимы в качестве параметра объекты типа
const, то порождаемый конструктор копирования для X будет иметь
единственный параметр типа const X& и записываться так:

           X::X(const X&)

    Иначе, у него будет единственный параметр типа X&:

           X::X(X&)

и инициализация копированием объектов типа const класса X будет
невозможна.
   Аналогично, если все базовые классы и члены класса X имеют
операцию присваивания, допускающую параметры типа const, тогда
порождаемая для X операция присваивания будет иметь единственный
параметр типа const X& и записываться так:

           X& X::operator=(const X&)

    Иначе, у нее будет единственный параметр типа X&:

           X& X::operator=(X&)

и присваивание копированием объектов класса X типа const будет
невозможно. Стандартная операция присваивания возвращает ссылку
на объект, который нужно было копировать.
    Объекты, представляющие виртуальные базовые классы, будут
инициализироваться только один раз с помощью порождаемого
конструктора копирования. Объекты, представляющие виртуальные
базовые классы, допускают присваивания им только один раз с помощью
порождаемой операции присваивания.
    Присваивание по членам и инициализация по членам означают
следующее: если класс X имеет в качестве члена класс M, для реализации
присваивания и инициализации члена используются операции присваивания
в M и конструктор копирования M соответственно. Если класс имеет
член типа const, или член, являющийся ссылкой, или член или базовый
класс такого класса, где функция operator=() является частной,
то для него стандартная операция присваивания не может быть создана.
Аналогично, если член или базовый класс класса M имеет частный
конструктор копирования, то стандартный конструктор копирования для
такого класса не может быть создан.
    Пока не появится необходимость в определении, стандартные присваивание
и конструктор копирования будут только описаны (т.е. не будет создано
тело функции). Иными словами, функция X::operator=() будет порождена
только тогда, когда нет явного описания операций присваивания, а объект
класса X присваивается объекту класса X или объекту класса, производного
от X, или вычисляется адрес функции X::operator=(). Аналогичная ситуация
с инициализацией.
    Если присваивание и конструктор копирования описаны неявно, то
они будут общими функциями-членами и операция присваивания для класса
X определяется таким образом, что ее результатом является ссылка
типа X& на объект, которому присваивают.
    Если в классе X есть функция X::operator=(), параметром которой
является сам класс X, то стандартное присваивание не будет
порождаться. Если в классе определен какой-либо конструктор
копирования, то стандартный конструктор копирования не будет
порождаться. Приведем пример:

         class X {
           // ...
         public:
           X(int);
           X(const X&, int = 1);
         };

         X a(1);     // вызов X(int)
         X b(a,0);   // вызов X(const X&,int)
         X c = b;    // вызов X(const X&,int)

    Присваивание объектов класса X определяется через функцию
X::operator=(const X&). Это означает ($$R.12.3), что объекты
производного класса можно присваивать объектам общего базового
класса, например:

         class X {
         public:
           int b;
         };

         class Y : public X {
         public:
           int c;
         };

         void f()
         {
           X x1;
           Y y1;
           x1 = y1;  // нормально
           y1 = x1;  // ошибка
         }

  В этом примере y1.b присваивается x1.b, а x1.c не копируется.
    Копирование одного объекта в другой с помощью стандартной
операции копирования или стандартного конструктора копирования
не изменяет структуру обоих объектов. Приведем пример:

          struct s {
            virtual f();
            // ...
          };

          struct ss : public s {
            f();
            // ...
          };

          void f()
          {
            s a;
            ss b;
            a = b;     // на самом деле выполняется a.s::operator=(b)
            b = a;     // ошибка
            a.f();     // вызов s::f
            b.f();     // вызов ss::f
            (s&)b = a; // присваивание a  b
                       // на самом деле выполняется ((s&)b).s::operator=(a)
            b.f();     // все еще вызов ss::f
          }

Вызов a.f() приведет к вызову s::f() (как и должно быть для объекта
класса s ($$R.10.2)), а вызов b.f() приведет к вызову ss::f()
( как и должно быть для объекта класса ss).

R.13 Перегрузка


Говорят, что имя перегружено, если для него задано несколько различных
описаний функций в одной области видимости. При использовании имени
выбор правильной функции производится путем сопоставления типов
формальных параметров с типами фактических параметров, например:

          double abs(double);
          int abs(int);

           abs(1);   // вызов abs(int)
           abs(1.0); // вызов abs(double)

Поскольку при любом типе T и для самого T , для и T& допустимо одно и
то же множество инициализирующих значений, функции, типы параметров
которых различаются только использованием, или не использованием
ссылки, не могут иметь одинаковые имена, например:

           int f(int i)
           {
             // ...
            }

            int f(int& r)  // ошибка: типы функций
            {              // недостаточно различны
              // ...
             }

Аналогично, поскольку для любом типе T для самого T, const T и
volatile T допустимо одно и то же множество инициализирующих
значений, функции, типы параметров которых отличаются только
указанной спецификацией, не могут иметь одинаковые имена. Однако,
различить const T&, volatile T& и просто T& можно, поэтому допустимы
определения функций с одним именем, которые различаются только
в указанном отношении. Аналогично, допустимы определения функций
с одним именем, типы параметров которых различаются только как
типы вида const T*, volatile T* и просто T*.
   Не могут иметь одинаковые имена функции, которые отличаются
только типом возвращаемого значения.
   Не могут иметь одинаковые имена функции-члены, одна из которых
статическая, а другая нет ($$R.9.4).
   С помощью конструкции typedef не создаются новые типы,
а только определяется синоним типа ($$R.7.1.3), поэтому функции,
которые отличаются только за счет использования типов, определенных с
помощью typedef, не могут иметь одинаковые имена. Приведем
пример:

        typedef int Int;

        void f(int i) { /* ... */ }
        void f(Int i) { /* ... */ } // ошибка: переопределение f

С другой стороны все перечисления считаются разными типами, и с их
помощью можно различить перегруженные функции, например:

        enum E { a };

        void f(int i) { /* ... */ }
        void f(E i)   { /* ... */ }

   Типы параметров, которые различаются только тем, что в одном
используется указатель *, а в другом массив [], считаются идентичными.
Напомним, что для типа параметра важны только второй и последующие
индексы многомерного массива ($$R.8.2.4). Подтвердим сказанное
примером:

        f(char*);
        f(char[]);       // идентично f(char*);
        f(char[7]);      // идентично f(char*);
        f(char[9]);      // идентично f(char*);
        g(char(*)[10]);
        g(char[5][10]);  // идентично g(char(*)[10]);
        g(char[7][10]);  // идентично g(char(*)[10]);
        g(char(*)[20]);  // отлично от g(char(*)[10]);

R.13.1 Сопоставление описаний


Два описания функций с одинаковыми именами относятся к одной и той
же функции, если они находятся в одной области видимости и имеют
идентичные типы параметров ($$R.13). Функция-член производного
класса относится к иной области видимости, чем функция-член
базового класса с тем же именем. Рассмотрим пример:

        class B {
        public:
          int f(int);
        };

        class D : public B {
        public:
          int f(char*);
        };

Здесь D::f(char*) скорее скрывает B::f(int), чем перегружает эту
функцию.

        void h(D* pd)
        {
          pd->f(1);     // ошибка: D::f(char*) скрывает B::f(int)
          pd->B::f(1);  // нормально
          pd->f("Ben"); // нормально, вызов D::f
        }

Функция, описанная локально, находится в иной области видимости, чем
функция с файловой областью видимости.

        int f(char*);
        void g()
        {
          extern f(int);
          f("asdf");  // ошибка: f(int) скрывает f(char*) поэтому
                      // в текущей области видимости нет f(char*)
        }

   Для разных вариантов перегруженной функции-члена можно задать
разные правила доступа, например:

         class buffer {
         private:
           char* p;
           int size;

         protected:
           buffer(int s, char* store) { size = s; p = store; }
           // ...

         public:
           buffer(int s) { p = new char[size = s]; }
         };

R.13.2 Сопоставление параметров


При вызове функции с данным именем происходит выбор из всех
функций с этим именем, которые находятся в текущей области видимости, и
для которых существуют преобразования типа, делающие вызов возможным.
Выбирается та функция, которая наиболее соответствует фактическим
параметрам. Она находится в области пересечения множеств
функций, каждое из которых наиболее соответствуют вызову по данному
фактическому параметру. Операция вызова считается допустимой, если в этом
пересечении находится только один член. Функция, выбранная таким образом,
должна более любой другой функции с тем же именем соответствовать
вызову, хотя бы по одному из параметров (необязательно это
будет один и тот же параметр для разных функций). В противном случае,
вызов считается недопустимым.
    При сопоставлении параметров рассматривают функцию с числом
стандартных значений параметров ($$R.8.2.6), равным n, как
n+1 функций с различным числом параметров.
   При сопоставлении параметров нестатическую функцию-член
рассматривают как функцию, имеющую дополнительный параметр,
указывающий на объект, для которого вызывается функция. Этот
дополнительный формальный параметр должен сопоставляться или
с объектом, или с указателем на объект, заданными в явной операции
вызова функции-члена ($$R.5.2.4), или же с первым операндом
перегруженной функции operator ($$R.13.4). Для этого дополнительного
параметра не используется никаких временных объектов, а для достижения
сопоставления не производится никаких пользовательских преобразований
типа.
   Если явно вызывается член класса X, используя указатель и операцию
->, то считается, что дополнительный параметр имеет тип const* X для
членов типа const, volatile* X для членов типа volatile и
X* для всех остальных членов. Если явно вызывается функция-член,
используя объект и операцию ., а также, если  вызывается функция
для первого операнда перегруженной функции operator ($$R.9.4),
то считается, что дополнительный параметр имеет тип: const X& для
членов типа const, volatile X& для членов типа volatile и  X&
для всех остальных членов. Первый операнд для ->* и .* рассматривается
так же, как и первый операнд для -> и . соответственно.
   Эллипсис в списке формальных параметров ($$R.8.2.5) может
сопоставляться с фактическим параметром любого типа.
   Для данного фактического параметра допускается только такая
последовательность преобразований типа, которая содержит не более
одного пользовательского преобразования. Ее нельзя сократить,
исключив одно или несколько преобразований, до последовательности,
которая также приводит к типу, сопоставимому с типом рассматриваемого
формального параметра. Такая последовательность преобразований
называется наиболее соответствующей последовательностью.
   Например, последовательность int->float->double задает
преобразование int в double, но ее нельзя назвать наиболее
соответствующей последовательностью, поскольку в ней содержится
более короткая последовательность int->double.
    Кроме описанных ниже случаев, следующие тривиальные
преобразования  типа T не влияют на свойство последовательности
быть наиболее соответствующей:

       исходный тип      тип результата
           T                 T&
           T&                T
           T[]               T*
           T(параметры)      T(*)(параметры)
           T                 const T
           T                 volatile T
           T*                const T*
           T*                volatile T*

Последовательности тривиальных преобразований, которые отличаются
только порядком преобразований, считаются совпадающими. Отметим,
что для функций с формальным параметром типа T, const T, volatile T,
T&, const T& и volatile T& допустим фактический параметр из одно и
того же множества значений. При необходимости для разделения
последовательностей преобразований используются спецификации const и
volatile, как описано в правиле [1] ниже.
   Для формального параметра типа T& требуется временная переменная
в случаях, если: фактический параметр не является адресом, или имеет тип,
отличный от T, в том числе тип volatile. Наличие такой переменной
не влияет на сопоставление параметров. Однако, оно может повлиять
на допустимость результата сопоставления, т.к. временную переменную
нельзя использовать для инициализации ссылок, не являющихся
const ($$R.8.4.3).
   Последовательности преобразований рассматриваются согласно
следующим правилам:
   [1] Точное сопоставление. Последовательности из нуля или более
       тривиальных преобразований предпочтительнее любых других
       последовательностей. Из более сложных последовательностей
       наиболее предпочтительны те, в которых нет преобразований
       T* в const T*, T* в volatile T*, T& в const T& или
       T& в volatile T&.
   [2] Сопоставление со стандартными преобразованиями основных типов.
       Из последовательностей, не относящихся к [1], наиболее
       предпочтительны те, которые содержат только стандартные
       целочисленные преобразования ($$R.4.1),
       преобразования float в double и тривиальные преобразования.
   [3] Сопоставление с любыми стандартными преобразованиями.
       из последовательностей, не относящихся к [2], наиболее
       предпочтительны те, которые содержат только любые
       стандартные преобразования ($$R.4.1, $$R.4.2, $$R.4.3, $$R.4.4,
       $$R.4.5, $$R.4.6, $$R.4.7, $$R.4.8) и тривиальные
       преобразования. Для этих последовательностей если A является
       прямым или косвенным общим базовым для класса B,
       то преобразование B* в A* предпочтительнее преобразования B*
       в void* или const void*. Далее, если B является прямым или
       косвенным базовым классом для C, то предпочтительнее преобразование
       C* в B*, чем C* в A*, и предпочтительнее преобразование C& в B&,
       чем C& в A&. Иерархия классов выступает здесь критерий отбора
       преобразований указателя в член ($$R.4.8).
   [4] Сопоставление с пользовательскими преобразованиями.
       Из последовательностей, не относящихся к [3], наиболее
       предпочтительны те, которые содержат только
       пользовательские ($$R.12.3), стандартные ($$R.4) и тривиальные
       преобразования.
   [5] Сопоставление с эллипсисом.
       Последовательности, которые требуют сопоставления с эллипсисом,
       считаются наименее предпочтительными.
   Пользовательские преобразования выбирают, исходя из типа
переменной, которая инициализируется или которой присваивается
значение.

        class Y {
          // ...
        public:
          operator int();
          operator double();
        };

        void f(Y y)
        {
          int i = y;    // вызов Y::operator int()
          double d;
          d = y;        // вызов Y::operator double()
          float f = y;  // ошибка: неоднозначность
        }

  Стандартные преобразования ($$R.4) могут применяться к параметру,
как до пользовательского преобразования, так и после него.

        struct S { S(long); operator int(); };

        void f(long), f(char*);
        void g(S), g(char*);
        void h(const S&), h(char*);

        void k(S& a)
        {
          f(a);    // f(long(a.operator int()))
          g(1);    // g(S(long(1)))
           h(1);   // h(S(long(1)))
        }

Если для параметра требуется пользовательское преобразование, то не
учитываются никакие стандартные преобразования, которые могут
затрагивать этот параметр, например:

        class x {
        public:
           x(int);
        };

        class y {
        public:
           y(long);
        };

        void f(x);
        void f(y);

        void g()
        {
          f(1);  // неоднозначность
        }

Здесь вызов f(1) неоднозначен. Несмотря на то, что для вызова
f(y(long(1))) требуется на одно стандартное преобразование больше,
чем для вызова f(x(1)), второй вызов не является предпочтительным.
   Преобразования с помощью конструктора ($$R.12.1) и с помощью
функции преобразования ($$R.12.3.2) равноправны.

        struct X {
          operator int();
        };

        struct Y {
          Y(X);
        };

        Y operator+(Y,Y);

        void f(X a, X b)
        {
           a+b;  // ошибка, неоднозначность:
                 //   operator+(Y(a), Y(b)) или
                 //   a.operator int() + b.operator int()
        }

R.13.3 Адрес перегруженной функции


Когда функция с некоторым именем используется без параметров, среди
всех функций с таким именем в текущей области видимости выбирается
единственная, которая точно соответствует назначению. Назначением
может быть:
       инициализируемый объект ($$R.8.4);
       левая часть операции присваивания ($$R.5.17);
       формальный параметр функции ($$R.5.2.2);
       формальный параметр пользовательской операции ($$R.13.4);
       тип значения, возвращаемого функцией ($$R.8.2.5).
  Отметим, что если f() и g() являются перегруженными функциями, то
для правильной интерпретации f(&g) или эквивалентного выражения
f(g) нужно рассмотреть пересечение множеств выбора для f() и g().
Приведем пример:

       int f(double);
       int f(int);
       int (*pfd)(double) = &f;
       int (*pfi)(int) = &f;
       int (*pfe)(...) = &f;   // ошибка: несоответствие типов

Последняя инициализация ошибочна, не из-за неоднозначности, а
потому, что не определено ни одной функции f() типа int(...).
   Отметим, что не существует никакого стандартного преобразования
($$R.4) указателя на функцию одного типа в указатель на функцию
другого типа ($$R.4.6). В частности, даже если B является общим
базовым классом D, две следующие инициализации недопустимы:

       D* f();
       B* (*p1)() = &f;     // ошибка

       void g(D*);
       void (*p2)(B*) = &g; // ошибка

R.13.4 Перегруженные операции


Перегружать можно большинство операций.

     имя-функции-оператор:
         operator операция

     операция: один из
               new delete
               +   -   *   /   %   ^   &   |   ~
               !   =   <   >   +=  -=  *=  /=  %=
               ^=  &=  |=  <<  >>  >>= <<= ==  !=
               <=  >=  &&  ||  ++  --  ,   ->* ->
               ()  []

Две последние операции - это вызов функции ($$R.5.2.2) и индексация
($$R.5.2.1).
    Можно перегружать следующие (как бинарные, так и унарные)
операции:

            +  -  *  &

    Нельзя перегружать следующие операции:

          .  .*  ::  ?:  sizeof

а также и специальные символы препроцессора # и ## ($$R.16).
  Обычно функции, задающие операции (функция-оператор) не вызываются
явно, к ним обращаются для выполнения операций ($$R.13.4.1, $$R.13.4.2).
Однако, к ним можно обращаться явно, например:

         complex z = a.operator+(b);  // complex z = a+b
         void* p = operator new(sizeof(int)*n);

  Операции new и delete описаны в $$R.5.3.3 и $$R.5.3.4 и к ним
не относятся перечисляемые ниже правила.
  Функция-оператор может быть функцией-членом или иметь по крайней
мере один параметр типа класс или ссылка на класс. Нельзя изменить
приоритет, порядок выполнения или число операндов операции, но
можно изменить предопределенное назначение таких операций:  =,
унарная & и ,(запятой), если они применяются к объекту типа класс.
За исключением функции operator=(), функция-оператор наследуется.
Правила для operator=() даны в $$R.12.8.
   Эквивалентность некоторых операций над основными типами
(например, ++a эквивалентно a+=1) может не сохраняться для таких
же операций над классами. Для некоторых операций требуется, чтобы
в случае использования основных типов операнд был адресом (например,
для +=). Это требование может быть снято, если операция задана над
классами.
   Перегруженная операция не может иметь стандартные значения
параметров ($$R.8.2.6).
   Операции, которые явно не указаны в $$R.13.4.3-$$R.13.4.7,
действуют как обычные унарные или бинарные операции, подчиняющиеся
правилам, приведенным в $$R.13.4.1 или $$R.13.4.2.

R.13.4.1 Унарные операции


Префиксную унарную операцию можно задать с помощью нестатической
функции-члена ($$R.9.3), без параметров или с помощью
функции, не являющейся членом, с одним параметром. Таким образом,
для всякой префиксной унарной операции @, выражение @x может
интерпретироваться как x.operator@() или как operator@(x).
Если описаны функции-операторы обоих видов, то какая из них будет
использоваться при вызове, определяется правилами сопоставления
параметров ($$R.13.2). Постфиксные унарные операции, такие как ++ и -- ,
объясняются в $$R.13.4.7.

R.13.4.2 Бинарные операции


Бинарную операцию можно задать с помощью нестатической
функции-члена ($$R.9.3), имеющей один параметр, или с помощью
функции, не являющейся членом, с двумя параметрами. Таким образом,
для всякой бинарной операции @ выражение x@y может интерпретироваться
как x.operator@(y) или как operator@(x,y). Если описаны
функции-операторы обоих видов, то какая из них будет использоваться
при вызове, определяется правилами сопоставления параметров ($$R.13.2).

R.13.4.3 Присваивания


Функция присваивания operator=() должна быть нестатической
функцией-членом. Она не наследуется ($$R.12.8). Более того, если
пользователь не определил для класса X функцию operator=, то
используется стандартная функция operator=, которая определяется
как присваивание по членам для класса X.

     X& X::operator=(const X& from)
     {
         // копирование по членам X
     }

R.13.4.4 Вызов функции


Вызов функции есть конструкция вида:
        первичное-выражение ( список-выражений opt )
Она считается бинарной операцией, в которой первичное-выражение
представляет первый операнд, а список-выражений (возможно пустой),
- второй операнд. Именем, задающим функцию, служит operator(), и вызов
x(arg1,arg2,arg3) для объекта класса x интерпретируется как
x.operator()(arg1,arg2,arg3). Функция operator() должна быть
нестатической функцией-членом класса x.

R.13.4.5 Индексация


Индексация, определяемая как:
       первичное-выражение [ выражение ]
считается бинарной операцией. Выражение с индексацией x[y] для объекта
класса x интерпретируется как x.operator[](y). Функция operator[]
должна быть нестатической функцией-членом класса x.

R.13.4.6 Доступ к члену класса


Доступ к члену класса определяется с помощью операции ->:
       первичное-выражение -> первичное-выражение
Он считается унарной операцией. Для объекта класса x выражение x->m
интерпретируется как (x.operator->())->m. Отсюда следует, что
функция operator->() должна возвращать или указатель на класс, или
ссылку на класс, или объект класса, для которого определена функция
operator->(). Она должна быть нестатической функцией-членом класса.

R.13.4.7 Инкремент и декремент


Функция с именем operator++ и с одним параметром задает для объектов
некоторого класса операцию префиксного инкремента ++. Функция с
именем operator++  и с двумя параметрами задает для объектов
некоторого класса операцию постфиксного инкремента ++. Для постфиксной
операции ++ второй параметр должен быть типа int, и,
когда в выражении встречается операция постфиксного инкремента,
функция operator++ вызывается со вторым параметром, равным нулю.
Приведем пример:

         class X {
         public:
           X operator++();   // префиксная ++a
           X operator++(int) // постфиксная a++
         };

         void f(X a)
         {
           ++a;    // a.operator++();
           a++;    // a.operator++(0);

           a.operator++();  // явный вызов: действует как ++a;
           a.operator++(0); // явный вызов: действует как a++;
         }

   Префиксные и постфиксные операции декремента -- определяются
аналогичным образом.


 R.14 ШАБЛОНЫ ТИПА

 R.14.1 Шаблоны типа

 Шаблон типа определяет целое семейство типов или функций.

        описание-шаблона-типа:
           template < список-параметров-шаблона-типа> описание

        список-параметров-шаблона-типа:
             параметр-шаблона-типа
             список-параметров-шаблона-типа , параметр-шаблона-типа

        параметр-шаблона-типа:
             параметр-типа
             описание-параметра

        параметр-типа:
             class идентификатор

Конструкция описание в описании-шаблона-типа должна содержать
описание или определение функции или класса.
  В конструкции параметр-типа идентификатор определяется как имя-типа
в области видимости описания шаблона типа.
  Имена шаблонов типа подчиняются обычным правилам для областей
видимости и контроля доступа. Конструкция описание-шаблона-типа
считается описанием. Она может присутствовать в программе только
как глобальное описание.

R.14.2 Шаблоны типов для классов


Шаблон типа для класса определяет как будут строиться  классы,  подобно
тому, как описание класса определяет как будут строиться объекты этого
класса. Шаблон типа для класса vector можно описать следующим образом:

          template<class T> class vector {
             T* v;
             int sz;
          public:
             vector(int);
             T& operator[](int);
             T& elem(int i) { return v[i] }
             // ...
          };

Префикс template<class T> показывает, что описывается шаблон типа,
и что в этом описании используется имя-типа T, иными словами,
vector - это параметризованный тип с параметром T.
   Класс можно задать с помощью конструкции имя-шаблонного-класса:
         имя-шаблонного-класса:
             имя-шаблона-типа < список-парам-шаблона-типа >

         список-парам-шаблона-типа:
             парам-шаблона-типа
             список-парам-шаблона-типа , парам-шаблона-типа

         парам-шаблона:
             выражение
             имя-типа
   Конструкция имя-шаблонного-класса  является именем-класса ($$R.9).
   Класс, который порождается шаблоном типа для класса, называется
шаблонным классом и эквивалентен обычному классу, определенному со
специальным именем - именем-шаблонного-класса, см. $$R.14.5.
   Если в конструкции имя-шаблонного-класса имя-шаблона-типа не определено,
то она обозначает неопределенный класс.
   Имя шаблонного класса должно быть уникальным в программе и в своей
области видимости оно не может обозначать другой шаблон типа,
класс, функцию, объект, значение или тип.
   Типы, указанные в списке-парам-шаблона-типа из
имени-шаблонного-класса, должны соответствовать типам, заданным
в списке-параметров-шаблона-типа из шаблона-типа. (Можно сказать,
что первые являются фактическими параметрами шаблона типа, а вторые
- формальными.)
   Кроме типов в списке-парам-шаблона-типа могут быть:
выражения-константы, адреса объектов или функций, подлежащих внешнему
связыванию, статические члены классов. Для параметров, не
являющихся типами, требуется точное соответствие ($$R.13.2).
   Приведем примеры использования классов шаблона типа vector:

          vector<int> v1(20);
          vector<complex> v2(30);

          typedef vector<complex> cvec; // cvec становится синонимом
                                        // vector<complex>
          cvec v3(40);  // v2 и v3 одного типа

          v1[3] = 7;
          v2[3] = v3.elem(4) = complex(7,8);

  Здесь vector<int> и vector<complex> являются шаблонными классами,
и их определения берутся по умолчанию из шаблона типа vector.
  Поскольку шаблонное-имя-класса является именем-класса, то оно может
использоваться там, где допустимо имя-класса, например:

          class vector<Shape*>

          vector<Window>* current_window;

          class svector : public vector<Shape*> { /* ... */ };

  Определение функции-члена шаблонного класса дано в $$R.14.6.

R.14.3 Эквивалентность типов


Две конструкции шаблонное-имя-класса обозначают один и тот же класс,
если в них совпадают имена шаблонов типа и значения указанных
параметров. Например, в следующих описаниях x и y одного типа,
который отличен от типа z:

          template<class E, int size> class buffer;

           buffer<char, 2*512> x;
           buffer<char,1024> y;
           buffer<char,512> z;

Ниже приведены описания, в которых одинаковый тип имеют x2 и x3.
Он отличается от типов x1 и x4:

           template<class T, void(*err_fct)()>
              class list { /* ... */ };

           list<int,&error_handler1> x1;
           list<int,&error_handler2> x2;
           list<int,&error_handler2> x3;
           list<char,&error_handler2> x4;

R.14.4 Шаблоны типа для функций


Шаблон типа для функции определяет как будет строиться функция. Например,
семейство функций sort можно описать следующим образом:

           template<class T> void sort(vector<T>);

Шаблон типа для функции порождает неограниченное множество перегруженных
функций. Функция, порождаемая шаблоном типа для функций, называется
шаблонной функцией. Она эквивалентна функции, в описании которой указан
тип, соответствующий шаблону, см. $$R.14.5.
   При вызове шаблонной функции параметры шаблона типа не задаются
явно, вместо этого применяется правило разрешения неопределенности
перегруженных функций. Рассмотрим пример:

           vector<complex> cv(100);
           vector<int> ci(200);

           void f(vector<complex>& cv, vector<int>& ci)
           {
             sort(cv);   // вызывается sort(vector<complex>)
             sort(ci);   // вызывается sort(vector<int>)
           }

    Шаблонная функция может быть перегружена как обычными, так и
шаблонными функциями с тем же именем. Для разрешения неопределенности
шаблонных и обычных функций с одним и тем же именем надо
последовательно пройти три шага:
   [1] Попытаться найти точно сопоставимую вызову ($$R.13.2) функцию,
       и если она найдена, вызвать ее.
   [2] Попытаться найти шаблон типа для функций, по которому
       можно создать точно сопоставимую с рассматриваемым вызовом
       функцию. Если удалось найти, то вызвать функцию.
   [3] Попытаться применить обычное правило разрешения неопределенности
       перегруженных функций ($$R.13.2). Если с его помощью
       функция найдена, вызвать ее.
Если не найдено сопоставимой функции, вызов является ошибочным.
Если уже на первом шаге найдено более одного кандидата,
сопоставимого с данной функцией, то вызов также считается неоднозначным
и ошибочным.
    Успешное выполнение шага [2] приведет к созданию некоторой
шаблонной функции с параметрами ($$R.14.5), типы которых точно
сопоставятся с типами параметров, указанных в вызове. В этом случае
недопустимо расхождение даже за счет тривиальных преобразований ($$R.13.2).
   Такие же действия применяются для сопоставления типов указателей
на функции ($$R.13.3).
   Рассмотрим пример:

         template<class T> T max(T a, T b) { return a>b?a:b; };

         void f(int a, int b, char c, char d)
         {
           int m1 = max(a,b);   // max(int a, int b)
           char m2 = max(c,d);  // max(char c, char b)
           int m3 = max(a,c);   // ошибка: нельзя создать max(int,char)
         }

   Добавив к этому примеру описание

         int max(int,int);

можно разрешить неопределенность для третьего вызова, поскольку теперь
задана функция, которая после стандартного преобразования char в int,
может сопоставиться с вызовом max(a,c).
   Определение  шаблона типа для функции используется для создания
различных вариантов шаблона типа. Для вызова определенного варианта
достаточно лишь описания шаблона типа.
   Каждый параметр-шаблона-типа, который приведен в
списке-параметров-шаблона-типа должен обязательно использоваться при
задании типов параметров в шаблоне типа для функции.

          template<class T> T* create(); //ошибка

          template<class T>
              void f() { // ошибка
                 T a;
                 // ...
              }

Все параметры-шаблона-типа, приведенные в шаблоне типа для функции,
должны быть параметрами-типа.

R.14.5 Описания и определения


Для каждого имени шаблона типа в программе должно существовать только
одно определение. Описаний может быть несколько. Определение
используется для создания специальных шаблонных классов и шаблонных
функций, которые будут соответствовать шаблону типа.
    Конструкция имя-шаблонного-класса вводит описание шаблонного класса.
    Вызов шаблонной функции или взятие ее адреса вводит описание
шаблонной функции. Для вызова или взятия адреса шаблонной функции
в языке существует особое соглашение: имя шаблонной функции
используется точно так же как имя обычной функции. Описание функции
с таким же именем, как у шаблонной функции, и с сопоставимыми типами
параметров, вводит описание специальной шаблонной функции.
    Если для выполнения некоторых операций требуется определение
специального шаблонного класса или специальной шаблонной функции,
и если такого определения в программе нет, то оно будет создано.
    Определение обычной (нешаблонной) функции с типом, который
точно сопоставляется с типом из описания шаблонной функции,
считается определением специальной шаблонной функции. Рассмотрим
пример:

          template<class T> void sort(vector<T>& v) { /* ... */ }

          void sort(vector<char*>& v) { /* ... */ }

Здесь определение функции sort будет использоваться для той функции
из семейства sort, которая сопоставляется при вызове с типом
параметра vector<char*>. Для других типов vector будет создаваться
соответствующее им определение функции по шаблону типа.
   Можно определить класс, который задает шаблонный класс, например:

          template<class T> class stream { /* ... */ };

         class stream<char> { /* ... */ };

Здесь описание класса будет использоваться в качестве определения
потока символов (stream<char>). Другие потоки будут управляться
с помощью шаблонных функций, создаваемых по шаблону типа для функций.
    Пока не появится описание шаблона типа для класса, никакие операции,
которым требуется определенный класс, не могут быть произведены над
шаблонным классом. После этого специальный шаблонный класс
будет считаться определенным, причем сразу же перед первым глобальным
описанием, использующим его имя.

R.14.6 Функции-члены шаблонов типа


Функция-член шаблонного класса считается неявной шаблонной функцией,
а параметры шаблона типа для ее класса - ее шаблонными параметрами.
Приведем пример, в котором описаны три шаблона типа для функции:

         template<class T> class vector {
            T* v;
            int sz;
         public:
            vector(int);
            T& operator[](int);
            T& elem(int i) { return v[i]; }
            // ...
         };

Функцию, выполняющую индексацию, можно определить следующим образом:

         template<class T> T& vector<T>::operator[](int i)
         {
           if (i<0 || sz>=i) error("vector: range error");
           return v[i];
         }

    Шаблонный параметр для vector<T>::operator[]() будет задаваться
тем типом vector, к которому применяется операция индексации.

         vector<int> v1(20);
         vector<complex> v2(30);

         v1[3] = 7;            // vector<int>::operator[]()
         v2[3] = complex(7,8); // vector<complex>::operator[]()

R.14.7 Друзья


Функция-друг для шаблона типа не является неявной шаблонной функцией,
например:

         template<class T> class task {
            // ...
            friend void next_time();
            friend task<T>* preempt(task<T>*);
            friend task* prmt(task*);  // ошибка
            // ...
         };

Здесь функция next_time() становится другом всех классов task, а
каждый класс task имеет в качестве друга функцию preempt() c
соответствующими типами параметров. Функцию preempt() можно
определить как шаблон типа.

         template<class T>
            task<T>* preempt(task<T>* t) { /* ... */ }

     Описание функции prmt() является ошибочным, поскольку типа
task не существует, а есть только специальные шаблонные типы
task<int>, task<record>, и т.д.

R.14.8 Статические члены и переменные


Для каждого шаблонного класса или функции, создаваемых по шаблону
типа, образуется своя копия статических переменных или членов.
Рассмотрим пример:

          template<class T> class X {
             static T s;
             // ...
          };

          X<int> aa;
          X<char*> bb;

Здесь в классе X<int> есть статический член типа int, а в классе
X<char> есть статический член типа char*.
   Аналогично, в приведенном ниже примере, функция f(int*) имеет
статический член s типа int, а функция f(char**) имеет статический
член типа char**:

          template<class T> f(T* p)
          {
             static T s;
             // ...
          }

          void g(int a, char* b)
          {
             f(&a);
             f(&b);
          }

R.15 Обработка особых ситуаций


R.15.1 Обработка особых ситуаций


При обработке особых ситуаций в ходе выполнения программы информация и
управление передаются из некоторой точки обработчику особых
ситуаций. Обработчик находится в цепочке выполненных вызовов
функций. Управление обработчику передается с помощью
выражения-запуска, которое может быть только в проверяемом-блоке
обработчика или в функции, вызванной из проверяемого-блока.

            проверяемый-блок:
                 try составной-оператор список-обработчиков

            список-обработчиков:
                 обработчик список-обработчиков opt

            обработчик:
                 catch ( описание-особой-ситуации ) составной-оператор

            описание-особой-ситуации:
                 список-спецификаций-типа описатель
                 список-спецификаций-типа абстрактный-описатель
                 список-спецификаций-типа
                 ...

            выражение-запуска:
                 throw выражение opt

Конструкция проверяемый-блок является оператором ($$R.6), а
выражение-запуска - унарным выражением типа void ($$R.5). Иногда
выражение-запуска называют "точкой запуска", а про функцию, в которой
встретилось выражение-запуска, говорят, что она "запускает особую
ситуацию. Часть программы, которой передается управление из точки
запуска называется обработчиком.

R.15.2 Запуск особой ситуации


При запуске особой ситуации управление передается обработчику. Запуск
сопровождается передачей объект, тип которого определяет, какой
обработчик должен перехватить особую ситуацию. Так, выражение

           throw "Help!";

может быть перехвачено некоторым обработчиком  с типом char*:

           try {
             // ...
           }
           catch(const char* p) {
            // здесь обрабатывается особая ситуация в символьных строках
           }

а особая ситуация Overflow (переполнение):

           class Overflow {
             // ...
           public:
             Overflow(char,double,double);
           };

           void f(double x)
           {
             // ...
             throw Overflow('+',x,3.45e107);
           }

может быть перехвачена обработчиком

          try {
                // ...
                f(1.2);
                // ...
          }
          catch(Overflow& oo) {
                // здесь обработка особой ситуации типа Overflow
          }

    При запуске особой ситуации управление передается ближайшему
обработчику соответствующего типа. "Ближайший" - это обработчик,
проверяемый-блок которого последним получил управление и оно еще
не было передано оттуда. Что такое "соответствующий" тип
определяется в $$R.15.4.
    При выполнении выражения-запуска создается временный объект
статического типа, который служит операндом в команде throw,
Этот объект используется для инициализации переменной,
соответствующего типа, описанной в обработчике. Если не считать
ограничений на сопоставление типов (см. $$R.15.4) и использование
 временной переменной, то операнд throw аналогичен параметру функции
 при вызове ($$R.5.2.2) или операнду в операторе return.
    Если можно было бы, не меняя смысла программы за счет отказа
от вызовов конструкторов и деструкторов для временного объекта
($$R.12.1), обойтись совсем без временного объекта, то особую
ситуацию можно было бы непосредственно инициализировать в обработчике
параметром выражения запуска.
    Если в выражении-запуска операнд не задан, то происходит
перезапуск обработки особой ситуации. Такое выражение-запуска
может появится только в самом обработчике или в функции, которая
непосредственно или опосредованно вызывается из него.
Например, фрагмент программы, который выполняется при обработке особой
ситуации, если нельзя еще полностью провести эту обработку, может
выглядеть так:

        try {
          // ...
        }
        catch (...) { // перехват всех особых ситуаций
         //  (частичная) обработка особых ситуаций
        throw; // передача остальных особых ситуаций другому обработчику

      }

R.15.3 Конструкторы и деструкторы


Когда управление передается из точки запуска особой ситуации
обработчику, то вызываются деструкторы для всех автоматических
объектов, построенных с момента входа в проверяемый-блок.
    Если объект не был построен полностью, то деструкторы
вызываются только для полностью построенных вложенных в него объектов.
Кроме того, если особая ситуация запускается в конструкторе при
построении элемента автоматического массива, то уничтожаться будут
только уже построенные элементы этого массива.
   Процесс вызова деструкторов для уничтожения автоматических
объектов, построенных в ходе выполнения программы от начала
проверяемого-блока до выражения-запуска, называется "раскручиванием
стека".

R.15.4 Обработка особой ситуации


Обработчик типа T, const T, T& или const& сопоставим с
выражением-запуска, имеющим операнд типа E, если:
   [1] T и E являются одним типом;
   [2] T является доступным ($$R.4.6) базовым классом E в точке
       запуска;
   [3] T является типом указателя, а E является таким типом указателя,
       который можно в точке запуска преобразовать в T с помощью
       стандартных преобразований указателя ($$R.4.6).
   Рассмотрим пример:

         class Matherr { /* ... */ virtual vf(); };
         class Overflow : public Matherr { /* ... */ };
         class Underflow : public Matherr { /* ... */ };
         class Zerodivide : public Matherr { /* ... */ };

         void f()
         {
           try {
              g();
           }

          catch (Overflow oo) {
            // ...
          }
          catch (Matherr mm) {
           // ...
          }
        }

Здесь обработчик Overflow будет перехватывать ситуации типа
Overflow, а обработчик Matherr будет перехватывать ситуации типа
Matherr и всех типов, являющихся общими производными от Matherr,
включая Underflow и Zerodivide.
   Обработчики в проверяемом-блоке подбираются для данной особой
ситуации в порядке их описания. Считается ошибкой , если обработчик
для базового класса помещен перед обработчиком для производного класса,
поскольку при таком расположении управление никогда не попадет
к обработчику для производного класса.
   Эллипсис ... в описании-особой-ситуации действует так же как, и
в описании параметров функции, он сопоставим с любой особой
ситуацией. Если задан эллипсис, то использующий его обработчик
должен идти последним в проверяемом-блоке.
   Если в проверяемом-блоке не произошло сопоставления ни с одним
из обработчиков, поиск соответствующего обработчика продолжается
в динамически объемлющем проверяемом-блоке. Если во всей программе
не произошло сопоставления ни с одним обработчиком, вызывается
функция terminate() ($$R.15.7).
   Особая ситуация считается обработанной после входа в тело
обработчика. В этот момент завершится "раскручивание стека".

R.15.5 Спецификации особых ситуаций


Возникновение и перехватывание особой ситуации влияет на
взаимодействие функций. Список особых ситуаций, которые прямо или
косвенно может запустить данная функция, можно задать как часть ее
описания. Конструкция спецификация-особой-ситуации
предшествует описателю функции.

         спецификация-особой-ситуации:
              throw ( список-типов opt )

         список-типов:
              имя-типа
              список-типов , имя-типа

Приведем пример:

         void f() throw (X,Y)
         {
           // ...
         }

  Если функция попробует запустить неуказанную в списке ситуацию,
управление передается функции unexpected(), см. $$R.15.8.
  Реализация языка не должна запрещать выражение только потому,
что при его вычислении возможен запуск особой ситуации, не указанной
в спецификации-особой ситуации описания функции. Обработка непредвиденных
особых ситуаций происходит в динамике.
  Функция, в которой отсутствует спецификация-особой-ситуации,
может запустить любую особую ситуацию.
  Функция с пустой спецификацией-особых-ситуаций (throw()) не должна
запускать никаких особых ситуаций.
  Если функция может запустить особую ситуацию класса X,  то она
может запустить особую ситуацию любого класса, являющегося общим
производным классом от X.
  Конструкция спецификация-особой-ситуации не относится к типу
функции.

R.15.6 Специальные функции


Механизм управления особыми ситуациями использует для реакции на ошибки
при самой обработке особых ситуаций функции:

terminate() и unexpected().

R.15.6.1 Функция terminate()


Иногда от предусмотренной обработки особых ситуаций приходится
переходить к более грубым приемам, например:
  - когда механизм управления особыми ситуациями не смог найти
    обработчик для запущенной особой ситуации;
  - когда механизм управления особыми ситуациями столкнулся
    с нарушенной структурой стека;
  - когда деструктор, вызванный в процессе раскрутки стека при запуске
    особой ситуации, сам пытается завершить выполнение программы,
    запустив особую ситуацию.
В этих случаях вызывается функция

    void terminate();

Она в свою очередь вызывает функцию, которая была указана как параметр
при последнем обращении к set_terminate():

    typedef void(*PFV)();
    PFV set_terminate(PFV);

Функция, которая была задана в предыдущем вызове set_terminate(),
будет возвращаемым значением текущего вызова. Это помогает
пользователю реализовать алгоритм восстановления стека. По умолчанию
функция terminate() вызывает abort().
   Выбор с помощью terminate() такой функции, которая вместо
действительного завершения программы, пытается вернуться в
вызвавшую программу, является ошибкой.

R.15.6.2 Функция unexpected()


Если функция, имеющая спецификацию-особой-ситуации, запускает
неуказанную особую ситуацию, то вызывается функция

      void unexpected();

Она в свою очередь вызывает функцию, которая была задана как
параметр при последнем обращении к set_unexpected():

      typedef void(*PFV)();
      PFV set_unexpected(PFV);

Функция, которая была задана в предыдущем вызове set_unexpected(),
будет возвращаемым значением текущего вызова. Это помогает
пользователю реализовать алгоритм восстановления стека. По умолчанию
unexpected() вызывает terminate(). Поскольку по умолчанию
terminate() вызывает abort(), результатом будет непосредственное
и точное обнаружение ошибки.

R.15.7 Особые ситуации и правила доступа


  Для формального параметра операции catch действуют такие же
правила доступа, как и для формального параметра функции, в которой
задана операция catch.
  При запуске особой ситуации можно указывать такой объект, который
можно копировать и уничтожать в области видимости функции, где
задана операция throw.


R.16 Препроцессорная обработка


Реализация языка С++ включает препроцессор с возможностями
макроподстановки, условной трансляции и включения указанных
файлов.
   Для передачи заданий препроцессору служат строки, начинающиеся
с символа # (перед ним могут идти пробелы и символы горизонтальной
табуляции). Такие строки называются командами, и их синтаксис
определяется независимо от остального языка. Команды могут
находиться в любом месте программы, и их действие продолжается
(независимо от правил областей видимости С++) до конца данной
единицы трансляции ($$R.2).
   Команду препроцессора, как и любую строку, можно продолжить на
следующей строке входного текста, поместив символ обратной
дробной черты непосредственно перед символом конца продолжаемой
строки. Препроцессор до того, как входная строка будет разбита на
лексемы, удаляет символы обратной дробной черты и конца строки.
Символ обратной дробной черты не должен быть последним символом
входного файла.
   К лексемам препроцессора относятся: лексемы самого языка ($$R.2.1),
имя файла, которое используется в команде #include и вообще любой
символ, отличный от обобщенного пробела и несовпадающий ни с какой
из лексем препроцессора.

R.16.1 Фазы препроцессорной обработки


По определению существует несколько фаз препроцессорной обработки.
В конкретной реализации фазы могут сливаться, но результат все равно
должен быть таким, как будто были выполнены все фазы.
  Перечислим их.

  При необходимости символы, зависящие от системы символы, обозначающие
конец строки, заменяются на стандартный символ конца строки. Аналогичной
замене подлежат все зависящие от системы символы. Определенные
последовательности символов (триграфы) заменяются на эквивалентный
им отдельный символ ($$R.16.2).

  Удаляются все такие пары символов: обратная дробная черта, следующий
за ней символ конца строки. В результате будут слиты строки входного
текста, из которых была удалена эта пара.

  Входной текст разбивается на лексемы препроцессора и последовательность
обобщенных пробелов. Каждый комментарий заменяется на один пробел.
Входной текст не должен кончаться посреди лексемы или комментария.

  Выполняются команды препроцессора, и производятся макроподстановки
($$R.16.3, $$R.16.4, $$R.16.5, $$R.16.6, $$R.16.7 и $$R.16.8).

  В символьных константах и строках литералов комбинации специальных
символов заменяются на свои эквиваленты ($$R.2.5.2).

 Сливаются соседние строки литералов.

 Результат препроцессорной обработки подвергается синтаксическому
и семантическому анализу, транслируется, а затем связывается с
необходимыми библиотеками и другими программами.

R.16.2 Триграфные последовательности


Прежде чем начнется какая-либо иная препроцессорная обработка,
каждое вхождение триграфной последовательности заменяется на один
символ в соответствии с приведенной ниже таблицей.

          ??=  #        ??(  [
          ??/  \        ??)  [
          ??'  ^        ??!  |

Например, строка

      ??=define arraycheck(a,b)  a??(b??) ??!??! b??(a??)

преобразуется в

      #define arraycheck(a,b) a[b] || b[a]

R.16.3 Макроопределение и макроподстановка


Команда вида

     #define идентификатор строка-лексем

называется макроопределением. Она указывает препроцессору, что надо
произвести замену всех последующих вхождений идентификатора на заданную
последовательность лексем, называемую строкой замены. Обобщенные
пробелы, окружающие эту последовательность лексем, отбрасываются.
Например, при определении

      #define SIDE 8

описание

      char chessboard[side][side];

после макроподстановки примет вид

      char chessboard[8][8];

Определенный таким способом идентификатор можно переопределить
с помощью другой команды #define, но при условии, что строки
замены в обоих определениях совпадают. Все символы обобщенного
пробела, разделяющие лексемы, считаются идентичными.
   Команда вида
идентификатор ( идентификатор, ... ,идентификатор) строка-лексем
называется макроопределением с параметрами или "функциональным"
макроопределением. В нем недопустимы пробелы между первым
идентификатором и символом (. Определенный таким способом
идентификатор можно переопределить с помощью другого функционального
макроопределения, но при условии, что во втором определении
то же число и те же наименования параметров, что и в первом, а обе
строки замены совпадают. Все символы обобщенного пробела, разделяющие
лексемы, считаются идентичными.
   Последующие вхождения идентификатора, определенного в
функциональном макроопределении, если за ним следуют символ
(, последовательность лексем, разделенных запятыми, и символ
), заменяются на строку лексем из макроопределения. Обобщенные
пробелы, окружающие строку замены, отбрасываются. Каждое вхождение
идентификатора, из списка параметров макроопределения, заменяется
на последовательность лексем, представляющую соответствующий
фактический параметр в макровызове. Фактическими параметрами являются
строки лексем, разделенные запятыми. Запятая, взятая в кавычки, или
находящаяся в символьной константе или во вложенных круглых скобках,
не разделяет параметров. Число фактических параметров макровызова должно
совпадать с числом параметров макроопределения.
   После идентификации параметров для функционального макроопределения
происходит подстановка фактических параметров. После выполнения подстановок
в параметре (если они были) этот параметр в строке замены замещается
фактическим параметром из макровызова ($$R.16.3.3); исключения
составляют случаи, когда параметру предшествует лексема # ($$R.16.3.1),
или с ним соседствует лексема ## ($$R.16.3.2).
    Приведем пример. Пусть есть макроопределения

       #define index_mask 0XFF00
       #define extract(word,mask)   word & mask

Тогда макровызов

       index = extract(packed_data,index_mask);

после подстановки примет вид

       index = packed_data & 0XFF00;

   Для обоих видов макроопределений строка замены проверяется  на
наличие других макроопределений ($$R.16.3.3).

R.16.3.1 Операция #


Если непосредственно перед параметром в строке замены идет лексема
#, то при подстановке параметр и операция # будут заменены
на строку литералов , содержащую имя соответствующего параметра
макровызова. В символьной константе или строке литералов, входящих
в параметр, перед каждым вхождением \ или " вставляется символ \.
     Например, если есть макроопределения

        #define path(logid,cmd) "/usr/" #logid "/bin/" #cmd

то макровызов

        char* mytool=path(joe,readmail);

приведет к такому результату:

        char* mytool="/usr/" "joe" "/bin/" "readmail";

После конкатенации соседних строк ($$R.16.1) получим:

        char* mytool="/usr/joe/bin/readmail";

R.16.3.2 Операция ##


Если в строке замены между двумя лексемами, одна из которых
представляет параметр макроопределения, появляется операция
##, то сама операция ## и окружающие ее обобщенные пробелы
удаляются. Таким образом, результат операции ## состоит в
конкатенации.
   Пусть есть макроопределение,

        #define inherit(basenum) public Pubbase ## basenum, \
                                 private Privbase ## basenum

тогда макровызов

        class D : inherit(1) { };

приведет к такому результату:

        class D : public Pubbase1, Privbase1 { };

   Макроопределение, которое в строке замены соседствует с
##, не подлежит подстановке, однако, результат конкатенации может
использоваться для подстановки. Приведем пример. Пусть есть
определения:

        #define concat(a)    a ## ball
        #define base         B
        #define baseball     sport

Тогда макровызов

        concat(base)

даст в результате

        sport

а вовсе не

        Bball

R.16.3.3 Повторный просмотр и дальнейшие подстановки


После того, как в строке замены произошла подстановка всех параметров
макровызова, получившаяся строка просматривается повторно для
обнаружения дополнительных макроопределений. Если в процессе
повторных просмотров строки замены найдено имя макроопределения, то
подстановка все же не происходит.
    Рекурсивную подстановку нельзя выполнить как команду
препроцессора, хотя она кажется для него естественной командой.

R.16.3.4 Область видимости макроимен и конструкция #undef


После появления макроопределения идентификатор из него считается
определенным и остается в текущей области видимости (независимо от
правил областей видимости в С++) до конца единицы трансляции или
пока его определение не будет отменено с помощью команды #undef.
   Команда #undef имеет вид:

        #undef идентификатор

Она заставляет препроцессор "забыть" макроопределение с этим
идентификатором. Если указанный идентификатор не является
определенным в данный момент макроименем, то команда #undef
игнорируется.

R.16.4 Включение файлов


Управляющая строка вида:

        #include <имяфайла>

приводит к замене данной строки на содержимое файла с указанным именем.
Поиск указанного файла проходит в определенной последовательности
частей архива системы и определяется реализацией.
    Аналогично, управляющая строка вида:

        #include "имяфайла"

приводит к замене данной строки на содержимое файла с указанным
именем. Поиск этого файла начинается в особых (системных) частях
архива, указанных в начале последовательности поиска. Если там он
не найден, то поиск файла идет по всей последовательности, как если бы
управляющая строка имела вид:

        #include <имяфайла>

   В имени файла, ограниченном символами < и > нельзя использовать
символы конца строки или >. Если в таком имени появится один из
символов ', \, или ", а также последовательность символов /* или //,
то результат считается неопределенным.
   В имени файла, ограниченном парой символов " нельзя использовать
символы конца строки или ", хотя символ > допустим. Если в таком
имени появится символ ' или \ или последовательность /* или //,
то результат считается неопределенным.
   Если команда

       #include строка-лексем

имеет вид, соответствующий ни первой, ни второй управляющей строке,
то лексемы препроцессора, заданные в этой команде обрабатываются как
обычный текст. В результате должна получиться команда, вид которой
соответствует одному из приведенных. Она и будет выполнена как положено.
  Команда #include может быть в файле, который сам появился в
результате выполнения другой команды #include.
  Реализация может накладывать ограничение на глубину вложенности
команды #include во входных файлах программы, которые приходится
читать для выполнения первоначальной команды #include в одном из
входных файлов.

R.16.5 Условная трансляция


С помощью препроцессора можно организовать условную трансляцию
программы. Синтаксически это задается следующим образом:

         условное:
               часть-if части-elif opt часть-else opt строка-endif

         часть-if:
               строка-if текст

         строка-if:
               # if выражение-константа
               # ifdef идентификатор
               # ifndef идентификатор

         части-elif:
               строка-elif текст
               части-elif строка-elif текст

         строка-elif:
               # elif выражение-константа

         часть-else:
               строка-else текст

         строка-else:
               # else

         строка-endif:
               # endif

    Константные выражения в #if и #elif (если эти части есть)
вычисляются в порядке их задания в тексте до тех пор, пока одно
из них не окажется отличным от нуля. Операторы С++, следующие за
строкой, в которой выражение оказалось равным нулю, не транслируются.
Команды препроцессора, идущие за этой строкой игнорируются.
После того, как найдена команда с ненулевым значением выражения,
текст всех последующих частей #elif и #else (т.е. операторы С++ и
команды препроцессора) игнорируется. Текст, относящийся к первой
команде с ненулевым значением выражения подлежит обычной препроцессорной
обработке и трансляции. Если значения всех выражений, указанных в #if
и #elif, оказались равными нулю, тогда обычной обработке подлежит текст,
относящийся к #else.
    В выражении-константе, которое встретилось в #if или #elif
можно использовать унарную операцию defined, причем в двух
вариантах:

            defined идентификатор

или

            defined (идентификатор)

Если эта операция применяется к идентификатору, который был определен
с помощью команды #define, и если это определение не было отменено
командой #undef, то результат равен 1, иначе результат равен 0.
Сам идентификатор defined нельзя переопределить, нельзя и отменить
его определение.
    После применения операций defined происходит раскрытие всех
всех макроопределений, имеющихся в константном выражении
см. $$R.16.3. В результате должно получиться целочисленное
выражение-константа, отличающееся от определения в $$R.5.19 тем,
что типы int и unsigned int рассматриваются как long и unsigned long
соответственно, а кроме того в этом выражении не должно быть
операций приведения, sizeof или элемента перечисления.
    Управляющая строка

       #ifdef идентификатор

эквивалентна строке

       #if defined идентификатор

а управляющая строка

       #ifndef идентификатор

эквивалентна строке

       #if !defined идентификатор

   Конструкции, задающие условную трансляцию, могут быть вложенными,
но реализация может накладывать ограничение на глубину вложенности
этих конструкций.

R.16.6 Управление строками


Для удобства написания программ, порождающих текст на С++, введена
управляющая строка вида:

       #line константа "имяфайла" opt

Она задает значение предопределенному макроимени __LINE__ ($$R.16.10),
которое используется в диагностических сообщениях или при
символической отладке; а именно: номер следующей строки
входного текста считается равным заданной константе, которая должна
быть десятичным целым числом. Если задано "имяфайла", то значение
макроимени __FILE__ ($$R.16.10) становится равным имени указанного
файла. Если оно не задано, __FILE__ не меняет своего значения.
   Макроопределения в этой управляющей строке раскрываются до
выполнения самой команды.

R.16.7 Команда error


Строка вида:

       #error строка-лексем

заставляет реализацию выдать диагностическое сообщение, состоящее
из заданной последовательности лексем препроцессора.

R.16.8 Команда pragma


Строка вида:

       #pragma строка-лексем

заставляет реализацию вести себя некоторым определенным образом
при условии что реализация  "понимает" эту строку. Любая
нераспознанная строка #pragma игнорируется.

R.16.9 Пустая директива


Команда препроцессора вида

       #

не оказывает никакого действия.

R.16.10 Предопределенные макроимена


В процессе трансляции определенную информацию содержат следующие
предопределенные макроимена.

 __LINE__ десятичная константа, содержащая номер текущей строки
          текста программы на С++.
 __FILE__ строка литералов, представляющая имя транслируемого
          входного файла.
 __DATE__ строка литералов, представляющая дату трансляции в виде
          "Mmm dd yyyy" или "Mmm d yyyy", если число меньше 10,
          (здесь Mmm задает месяц, dd - день, а yyyy - год).
 __TIME__ строка литералов, представляющая время трансляции в виде
          "hh:mm:ss", (здесь hh задает часы, mm - минуты, а
          ss - секунды).

Кроме того, считается определенным при трансляции программы на С++
макроимя __cplusplus.
Перечисленные макроимена нельзя как переопределять, так и отменять их
определения.
Макроимена __LINE__ и __FILE__ можно определить с помощью команды
#line ($$R.16.6).
Определено ли макроимя __STDC, и если да, то каково его значение,
зависит от реализации.

R.17 Приложение A: Сводка грамматики


Это приложение не относится к справочному руководству языка и
не является определением конструкций C++.
   Она только должно служить более полному пониманию С++. Нельзя
рассматривать его как точное определение языка, так как описанная здесь
грамматика допускает произвольное множество конструкций, каждая из
которых законна для С++. Чтобы различать выражения и описания,
следует применять правила разрешения неопределенности ($$r.6.8,
$$R.7.1, $$R.10.1.1). Далее, для отсеивания синтаксически правильных,
но бессмысленных, конструкций следует применять правила контроля
доступа, разрешения неопределенности и контроля типа.

R.17.1 Служебные слова


В описаниях: typedef ($$R.7.1.3), класса ($$R.9), перечисления
($$R.7.2),  шаблона типа - ($$R.14) введены новые, зависящие от
контекста, служебные слова, а именно:

        имя-класса:
             идентификатор

        имя-перечисления:
             идентификатор

        имя-typedef:
             идентификатор

Отметим, что имя-typedef, обозначающее класс, является в то же
время конструкцией имя-класса ($$R.9.1).

R.17.2 Выражения


       выражение:
            выражение-присваивания
            выражение, выражение-присваивания

       выражение-присваивания:
         выражение-условия
         унарное-выражение операция-присваивания выражение-присваивания
         операция-присваивания: один из
              =  *=  /=  %=  +=  -=  >>=  <<=  &=  ^=  |=
       выражение-условия:
             логическое-выражение-ИЛИ
             логическое-выражение-ИЛИ ? выражение : выражение-условия

        логическое-выражение-ИЛИ:
             логическое-выражение-И
             логическое-выражение-ИЛИ || логическое-выражение-И

        логическое-выражение-И:
             выражение-ИЛИ
             логическое-выражение-И && выражение-ИЛИ

        выражение-ИЛИ:
             выражение-исключающего-ИЛИ
             выражение-ИЛИ | выражение-исключающего-ИЛИ

          выражение-исключающего-ИЛИ:
               выражение-И
               выражение-исключающего-ИЛИ ^ выражение-И

          выражение-И:
               выражение-равенства
               выражение-И & выражение-равенства

          выражение-равенства:
               выражение-отношения
               выражение-равенства == выражение-отношения
               выражение-равенства != выражение-отношения

          выражение-отношения:
               сдвиговое-выражение
               выражение-отношения <  сдвиговое-выражение
               выражение-отношения >  сдвиговое-выражение
               выражение-отношения <= сдвиговое-выражение
               выражение-отношения >= сдвиговое-выражение

           сдвиговое-выражение:
               аддитивное-выражение
               сдвиговое-выражение << аддитивное выражение
               сдвиговое-выражение >> аддитивное выражение

           аддитивное-выражение:
               мультипликативное-выражение
               аддитивное выражение + мультипликативное-выражение
               аддитивное-выражение - мультипликативное-выражение

           мультипликативное-выражение:
               выражение-pm
               мультипликативное-выражение * выражение-pm
               мультипликативное-выражение / выражение-pm
               мультипликативное-выражение % выражение-pm

           выражение-pm:
               выражение-приведения
               выражение-pm .*  выражение-приведения
               выражение-pm ->* выражение-приведения

           выражение-приведения:
               унарное-выражение
               ( имя-типа ) выражение-приведения

           унарное-выражение:
                постфиксное-выражение
                ++ унарное выражение
                -- унарное выражение
                унарная-операция выражение-приведения
                sizeof унарная-операция
                sizeof ( имя-типа )
                выражение-размещения
                выражение-освобождения

          унарная-операция: один из
                *  &  +  -  !  ~

           выражение-размещения:
               ::opt new параметры-new opt имя-типа-new инициализатор-new
               ::opt new параметры-new opt ( имя-типа ) инициализатор-new

          параметры-new:
                ( список-выражений )

          имя-типа-new:
               список-спецификаций-типа описатель-new opt

          описатель-new:
               * список-спецификаций-cv opt описатель-new opt
               имя-класса :: список-спецификаций-cv opt описатель-new opt
               описатель-new opt [ выражение ]

          инициализатор-new:
                ( список-инициализаторов opt )

          выражение-освобождения:
                ::opt delete выражение-приведения
                ::opt delete [] выражение-приведения

          постфиксное-выражение:
                первичное-выражение
                постфиксное-выражение [ выражение ]
                постфиксное-выражение ( список-выражений opt )
                имя-простого-типа     ( список-выражений opt )
                постфиксное-выражение .  имя
                постфиксное-выражение -> имя
                постфиксное-выражение ++
                постфиксное-выражение --

          список-выражений:
                выражение-присваивания
                список-выражений , выражение-присваивания

         первичное-выражение:
                литерал
                this
                :: идентификатор
                :: имя-функции-операции
                :: уточненное-имя
                ( выражение )
                имя

          имя:
               идентификатор
               имя-функции-операции
               имя-функции-преобразования
               ~имя-класса
               уточненное-имя

          уточненное-имя:
               уточняющее-имя-класса :: имя

          литерал:
              целая константа
              символьная константа
              константа с плавающей точкой
              строка литералов

R.17.3 Описания


       описания:
              спецификации-описания opt список-описателей opt ;
              описание-asm
              определение-функции
              спецификация-связи

        спецификация-описания:
             спецификация-класса-памяти
             спецификация-типа
             спецификация-fct
             спецификация-шаблона-типа
             friend
             typedef

        спецификации-описания:
             спецификации-описания opt спецификация-описания

      спецификация-класса-памяти:
             auto
             register
             static
             extern

      спецификация-fct:
            inline
            virtual

     спецификация-типа:
           имя-простого-типа
           спецификация-класса
           спецификация-перечисления
           спецификация-сложного-типа
           :: имя-класса
           const
           volatile

     имя-простого-типа:
           полное-имя-класса
           уточненное-имя-типа
           char
           short
           int
           long
           signed
           unsigned
           float
           double
           void

     спецификация-сложного-типа:
           служебное-слово-класса имя-класса
           служебное-слово-класса идентификатор

     служебное-слово-класса:
           class
           struct
           union

     уточненное-имя-типа:
           имя-typedef
           имя-класса :: уточненное-имя-типа

     полное-имя-класса:
           уточненное-имя-класса
           :: уточненное-имя-класса

     уточненное-имя-класса:
           имя-класса
           имя-класса :: уточненное-имя-класса

      имя-перечисления:
           идентификатор

      спецификация-перечисления:
          enum идентификатор opt { список-перечисления }

      список-перечисления:
          элемент-перечисления
          список-перечисления , элемент-перечисления

      элемент-перечисления:
          идентификатор
          идентификатор = выражение-константа

     спецификация-связи:
         extern строка-литерал { список-описаний opt }
         extern строка-литерал описание

     список-описаний:
         описание
         список-описаний описание

      описание-asm:
          asm ( строка-литерал) ;

R.17.4 Описатели


      список-описаний:
           описатель-с-инициализатором
           список-описаний , описатель-с-инициализатором

      описатель-с-инициализатором:
           описатель инициализатор opt

      описатель:
          имя-в-описателе
          операция-ptr описатель
          описатель (список-описаний-параметров) список-спецификаций-cv opt
          описатель [ выражение-константа opt]
          ( описатель )

   операция-ptr:
         * список-спецификаций-cv opt
         & список-спецификаций-cv opt
         полное-имя-класса :: * список-спецификаций-cv opt

   список-спецификаций-cv:
         const
         volatile

   имя-в-описателе:
         имя
         имя-класса
         ~имя-класса
         имя-typedef
         уточненное-имя-типа

   имя-типа:
        список-спецификаций-типа абстрактный-описатель opt

   список-спецификаций-типа:
        спецификация-типа список-спецификаций-типа

   абстрактный-описатель:
        операция-ptr абстрактный-описатель opt
   абстрактный-описатель opt ( список-описаний-параметров ) список-спецификаций_cv opt
       абстрактный-описатель opt [ выражение-константа opt ]
       ( абстрактный-описатель )

   список-описаний-параметров:
        список-описаний-парам opt ... opt
        список-описаний-парам , ...

   список-описаний-парам:
        описание-параметра
        список-описаний-парам , описание-параметра

   описание-параметра:
        спецификации-описания описатель
        спецификации-описания описатель = выражение
        спецификации-описания абстрактный-описатель opt
        спецификации-описания абстрактный-описатель opt = выражение

   определение-функции:
        спецификации-описания opt описатель инициализатор-ctor тело-функции

   тело-функции:
        составной-оператор

   инициализатор:
         = выражение-присваивания
         = { список-инициализаторов , opt }
         ( список-выражений )

   список-инициализаторов:
         выражение-присваивания
         список-инициализаторов , выражение-присваивания
         { список-инициализаторов , opt }

R.17.5 Описания класса


    спецификация-класса:
         заголовок-класса { список-членов opt }

    заголовок-класса:
         служебное-слово-класса идентификатор opt спец-базовых opt
         служебное-слово-класса имя-класса спец-базовых opt

    служебное-слово-класса:
         class
         struct
         union

   список-членов:
         описание-члена список-членов opt
         спецификация-доступа : список-членов opt

   описание-члена:
         спецификации-описания opt список-описателей-членов opt ;
         определение-функции ; opt
         уточненное-имя ;

   список-описателей-членов:
         описатель-члена
         список-описателей-членов , описатель-члена

   описатель-члена:
         описатель спецификация-чистой opt
         идентификатор opt : выражение-константа

   спецификация-чистой:
         = 0

   список-базовых:
         спецификация-базовых
         список-базовых , спецификация-базовых

   спецификация-базовых:
         полное-имя-класса
         virtual спецификация-доступа opt полное-имя-класса
         спецификация-доступа virtual opt полное-имя-класса

   спецификация-доступа:
         private
         protected
         public

    имя-функции-преобразования:
         operator имя-типа-преобразования

    имя-типа-преобразования:
          список-спецификаций-типа операция-ptr opt

    инициализатор-ctor:
         : список-инициализаторов-членов

    список-инициализаторов-членов:
         инициализатор-члена
         инициализатор-члена , список-инициализаторов-члена

    инициализатор-члена:
         полное-имя-класса ( список-выражений opt )
         идентификатор

     имя-функции-оператор:
         operator операция

     операция: один из
               new delete
               +   -   *   /   %   ^   &   |   ~
               !   =   <   >   +=  -=  *=  /=  %=
               ^=  &=  |=  <<  >>  >>= <<= ==  !=
               <=  >=  &&  ||  ++  --  ,   ->* ->
               ()  []

R.17.6 Операторы


     оператор:
         помеченный-оператор
         оператор-выражение
         составной-оператор
         выбирающий-оператор
         оператор-цикла
         оператор-перехода
         оператор-описания

     помеченный-оператор:
         идентификатор : оператор
         case выражение-константа : оператор
         default : оператор

    оператор-выражение:
         выражение opt ;

    составной-оператор:
         { список-операторов opt }

    список-операторов:
         оператор
         список-операторов оператор

    выбирающий-оператор:
         if ( выражение ) оператор
         if ( выражение ) оператор else оператор
         switch ( выражение ) оператор

    оператор-цикла:
          while ( выражение ) оператор
          do оператор  while (выражение)
          for ( оператор-иниц выражение opt ; выражение opt ) оператор

    оператор-иниц:
          оператор-выражение
          оператор-описание

     оператор-перехода:
           break ;
           continue ;
           return выражение opt ;
           goto идентификатор ;

     оператор-описания:
           описание

R.17.7 Препроцессор


      #define идентификатор строка-лексем
      #define идентификатор ( идентификатор , ... , идентификатор ) строка-лексем

      #include "имяфайла"
      #include <имяфайла>

      #line константа "имяфайла" opt
      #undef идентификатор

      условное:
           часть-if части-elif opt часть-else opt строка-endif

      часть-if:
           строка-if текст

      строка-if:
           # if выражение-константа
           # ifdef идентификатор
           # ifndef идентификатор

       части-elif:
            строка-elif текст
            части-elif строка-elif текст

       строка-elif:
            # elif выражение-константа

       часть-else:
            строка-else текст

       строка-else:
            # else

       строка-endif:
            # endif

R.17.8 Шаблоны типа


        описание-шаблона-типа:
             template < список-параметров-шаблона-типа> описание

        список-параметров-шаблона-типа:
             параметр-шаблона-типа
             список-параметров-шаблона-типа , параметр-шаблона-типа

        параметр-шаблона-типа:
             параметр-типа
             описание-параметра

        параметр-типа:
             class идентификатор

         имя-шаблонного-класса:
             имя-шаблона-типа < список-парам-шаблона-типа >

         список-парам-шаблона-типа:
             парам-шаблона-типа
             список-парам-шаблона-типа , парам-шаблона-типа

         парам-шаблона:
             выражение
             имя-типа

R.17.9 Обработка особых ситуаций


         проверяемый-блок:
             try составной-оператор список-обработчиков

         список-обработчиков:
             обработчик список-обработчиков opt

         обработчик:
             catch ( описание-особой-ситуации ) составной-оператор

         описание-особой-ситуации:
             список-спецификаций-типа описатель
             список-спецификаций-типа абстрактный-описатель
             список-спецификаций-типа
                 ...

        выражение-запуска:
            throw выражение opt

        спецификация-особой-ситуации:
            throw ( список-типа opt )

        список-типа:
            имя-типа
            список-типа , имя-типа

R.18 Приложение B: Совместимость


Это приложение не относится к справочному руководству С++ и не
является определением конструкций языка.
   Язык С++ основывается на С (описание в книге Кернигана и Ритчи,
78 г., дальше K&R) и включает большинство изменений, предложенных
в ANSI стандарте для С. При конвертировании программ на языках
С++, K&R C и ANSI C могут возникнуть трудности в связи с различным
вычислением в них выражений. Транслятор должен распознавать все различия
между С++ и ANSI C. Программы на С++ и ANSI C должны иметь одинаковый
смысл за исключением трех следующих случаев:
   В языке С выражение sizeof('a') равно sizeof(int), а в С++
оно равно sizeof(char).
   Если  есть описание

       enum e { A };

то sizeof(A) равно в С sizeof(int), тогда как в С++ оно равно sizeof(e)
и не обязано быть равно sizeof(int).
   Имя структуры, описанной во внутреннем блоке, может скрывать имя
объекта, функции, элемента перечисления или типа из внешнего блока.
Приведем пример:

       int x[99];
       void f()
       {
         struct x { int a; };
         sizeof(x);  /* для C это размер массива   */
                     /* а для C++ размер структуры */
        }

R.18.1 Расширения


В этом разделе перечисляются основные расширения языка С, введенные
в С++.

R.18.1.1 Возможности С++, введенные в 1985 г.


Здесь перечисляются возможности, добавленные к С, версией языка
С++ 1985 г.
   Можно указывать типы формальных параметров функции ($$R.8.2.5), и они
будут проверяться ($$R.5.2.2). Будет происходить преобразование
типа ($$R.5.2.2). Это есть и в ANSI C.
   В выражениях со значениями типа float вычисления могут проходить
с обычной точностью ($$R.3.6.1 и $$R.4.3). Это есть и в ANSI C.
   Можно перегружать имена функций; $$R.13.
   Можно перегружать операции; $$R.13.4
   Возможна реализация вызова функций подстановкой; $$R.7.1.2.
   Можно описывать объекты, представляющие данные, со спецификацией
   const; $$R.7.1.6. Это есть и в ANSI C.
   Можно описывать типа ссылки; $$R.8.2.2 и $$R.8.4.3.
   Возможно управление свободной памятью с помощью операций new и
   delete; $$R.5.3.3 и $$R.5.3.4.
   Введены классы, которые позволяют: скрывать информацию ($$R.11),
   проводить инициализацию ($$R.12.1), осуществлять пользовательские
   преобразования типа ($$R.12.3) и работать с динамическими типами
   с помощью виртуальных функций ($$R.10.2).
   Имя класса или перечисления считается именем типа; $$R.9.
   Указатель на любой объект c типом, не являющимся const или volatile,
   можно присвоить указателю типа void*. Это есть и в ANSI C.
   Указатель на функцию можно присваивать указателю типа void*;
   $$R.4.6.
   Описание внутри блока считается оператором; $$R.6.7.
   Можно описывать безымянные объединения; $$R.9.5.

R.18.1.2 Возможности, добавленные в С++ после 1985 г.


Здесь перечисляются основные расширения С++ после 1985 г.:
   Класс может иметь более одного прямого базового класса
(множественное наследование); $$R.10.1.
   Члены класса могут быть защищенными; $$R.11.
   Операции new и delete можно описывать в классе и перегружать;
   $$r.5.3.3, $$R.5.3.4, $$R.12.5. Это позволило определенный способ
управления памятью для класса с помощью "присваивания указателю
this" отнести в раздел анахронизмов; $$R.18.3.3.
   Можно явно уничтожать объекты; $$R.12.4.
   Присваивания и инициализация для класса определены как присваивание и
инициализация по членам; $$R.12.8.
   Служебное слово overload стало излишним и отнесено к разделу
анахронизмов; $$R.18.3.
   Произвольные выражения разрешены в качестве инициализаторов статических
объектов; $$R.8.4.
   Объекты, представляющие данные, могут быть volatile; $$R.7.1.6.
Также и в ANSI C.
   Допустимы инициализаторы для статических членов класса; $$R.9.4.
   Функции-члены могут быть статическими; $$R.9.4.
   Функции-члены могут быть const или volatile; $$R.9.3.1.
   Можно явно указать связывание с подпрограммами на других языках;
$$R.7.4.
   Можно перегружать операции ->, ->* и ` ; $$R.13.4.
   Классы могут быть абстрактными; $$R.10.3.
   Для пользовательских типов префиксные и постфиксные операции
различаются.
   Шаблоны типов; $$R.14.
   Управление особыми ситуациями; $$R.15.

R.18.2 С++ и ANSI C


Вообще язык С++ обладает большими возможностями и налагает меньше
ограничений, чем ANSI C, поэтому большинство конструкций ANSI C
являются законными для С++, причем смысл их не меняется. Исключения
сводится к следующему:
   Любая программа на ANSI C, использующая в качестве идентификаторов
следующие служебные слова С++, не является программой на С++; $$R.2.4:

       asm       catch      class       delete     friend
       inline    new        operator    private    protected
       public    template   try         this       virtual
       throw

   Хотя это считается устаревшем в ANSI C, реализация С может налагать
 драконовские ограничения на длину идентификаторов; в реализациях С++
 это недопустимо; $$R.2.3.
   В С++ функция должна быть описана прежде, чем ее можно вызвать;
 $$R.5.2.2.
   Описание f(); в С++ означает, что функция f не имеет параметров
 ($$R.8.2.5), а в С это означает, что f может иметь любое число
 параметров любого типа. Такое описание считается устаревшим в ANSI C.
   В ANSI C можно несколько раз описать без спецификации extern глобальный
 объект данных, в С++ возможно только одно его определение; $$R.3.3
   В С++ класс не может иметь тоже имя, что и имя typedef, относящееся
 в той же области видимости к другому типу; $$R.9.1.
   В ANSI C операнд типа void* можно использовать в правой части
 присваивания, а также при инициализации переменной типа указателя на
 произвольный тип; в С++ это невозможно $$R.7.1.6.
   В ANSI C возможны команды переходов, обходящие инициализацию;
 в С++ это невозможно.
   В ANSI C по умолчанию глобальный объект типа const подлежит
 внешнему связыванию; для С++ это не так; $$R.3.3.
   Определения функций в "старом" стиле и вызовы неописанных функций
 считаются в С++ анахронизмами, которые не обязательно должны
 поддерживаться любой реализацией; $$R.18.3.1. В ANSI C они просто
 считаются устаревшими.
   В С++ структура (struct) образует область видимости ($$R.3.2);
 В ANSI C структура, перечисление или элемент перечисления,
 описанные в структуре поднимаются в область видимости самой
 структуры.
   Присваивание объекту типа перечисления значения, не принадлежащего
 перечислению, считается в С++ анахронизмом и не должно поддерживаться
 во всех реализациях; $$R.7.2. В ANSI C рекомендуется для таких
 присваиваний выдавать предупреждение.
   Строки, инициализирующие символьные массивы, не могут быть длиннее
 этих массивов; $$R.8.4.2.
   Тип символьной константы в С++ есть char ($$R.2.5.2) и int в
 ANSI C.
   Тип элемента перечисления есть тип этого перечисления в С++ ($$R.7.2)
 и тип int в ANSI C.
   Кроме того, стандарт ANSI для С допускает значительные различия в
допустимых реализациях языка, что может привести к еще большим расхождениям
между реализациями С++ и С. В частности, в некоторых реализациях С
могут быть допустимы некоторые несовместимые описания. В С++ требуется
совместимость даже для разных единиц трансляции; $$R.3.3.

R.18.2.1 Как бороться с расхождениями


В общем случае программа на С++ использует многие возможности,
отсутствующие в ANSI C. Для такой программы незначительные расхождения,
перечисленные в $$R.18.2, явно перекрываются расширениями в С++. Когда
С++ и ANSI C должны иметь общие заголовочные файлы, нужно позаботиться,
чтобы эти файлы представляли текст на общем подмножестве этих языков.
   Нельзя пользоваться специфическими возможностями С++ такими, как
классы, перегрузка и т.п.
   Нельзя использовать одно имя для обозначения типа структуры и другого
типа.
   Функцию без параметров следует описывать как f(void), а не просто f().
   Глобальные объекты типа const следует явно специфицировать как static
или extern.
   Для разделения частей программы на ANSI C и С++ можно использовать
условную трансляцию с предописанным именем __cplusplus.
   Функции, которые могут вызываться из программ на обеих языках, должны
быть явно описаны, как функции, подлежащие связыванию с С.

R.18.3 Анахронизм


Реализация С++ может включать перечисленные здесь расширения, чтобы
облегчить использование программы на С, или чтобы упростить переход
с более ранних версий С++. Отметим, что с каждым расширением связаны
нежелательные последствия. Если реализация предоставляет такое расширение,
то она должно также предоставлять возможность убедиться в отсутствии
этих последствий для исходной программы. Реализация С++ не обязана
обеспечивать эти расширения.
   При описании или определении функции можно использовать слово overload
 в конструкции спецификация-описания ($$R.7). Если оно используется в
спецификации-описания, то считается служебным словом и его нельзя
использовать как идентификатор.
   Определение статического члена класса, представляющего данные,
для которого дана стандартная инициализация нулями ($$R.8.4, $$R.9.4),
может быть опущено.
   Можно использовать команды препроцессора старого стиля (до ANSI C).
   Можно присваивать объекту типа перечисления значение типа int.
   При удалении массива, тип которого не имеет деструктора, можно
указывать число элементов; $$R.5.3.4.
   Одна функция operator++() может использоваться для перегрузки как
префиксных, так и постфиксных операций ++; тоже верно для операции --;
$$R.13.4.6.

R.18.3.1 Определения функций старого стиля


Можно использовать синтаксис С для определений функций:

     старое-определение-функции:
          спецификации-описаний opt старый-описатель-функции
          список-описаний opt тело-функции

     старый-описатель-функции:
          описатель ( список-параметров opt )

     список-параметров:
          идентификатор
          список-параметров , идентификатор

 Приведем пример:

 max(a,b) int b;  { return (a<b) ? b : a; }

 Если определенная таким образом функция не была описана ранее, то тип
 ее формальных параметров полагается (...), т.е. он не будет проверяться.
 Если она была описана, то тип должен согласовываться с типом,
 указанным в описании.
 Приведенный синтаксис нельзя использовать для определения функций-членов.

 R.18.3.2 Старый стиль задания инициализатора базового класса

 В конструкции инициализатор-памяти ($$R.12.6.2) можно не указывать
имя-класса, обозначающее базовый класс при условии, что существует
только один прямой (непосредственный) базовый класс. Поэтому в
описании

       class B {
          // ...
       public:
          B(int);
       };

       class D : public B {
          // ...
          D(int i) : (i) { /* ... */ }
       };

 будет вызываться конструктор B с параметром i.

 R.18.3.3 Присваивание указателю this

 Присваивая определенные значения указателю this, пользователь мог
 управлять выделением памяти для объекта некоторого класса. В
 конструкторе до использования членов класса можно было с помощью
 такого присваивания реализовать свой алгоритм выделения памяти.
 Присваивая в деструкторе указателю this нуль, можно было
 обойти стандартную операцию освобождения объектов класса. Кроме
 того, присваивание нуля в деструкторе отменяло неявные вызовы
 деструкторов для членов и базовых классов, например:

        class Z {
          int z[10];
          Z()  { this = my_allocator(sizeof(Z) ); }
          ~Z() { my_deallocator (this); this = 0; }
        };

   Если выделение памяти уже произошло (как бывает для членов и
объектов auto или static), то при входе в конструктор this имеет
ненулевое значение и значение нуль в противном случае.
   Вызовы конструкторов для членов и базовых классов произойдут
только после того, как this получил значение. Если в конструкторе
базового класса есть присваивание this, то новое значение this
будет использоваться и в конструкторах производных классов, если
они есть.
   Отметим, что при наличии указанного анахронизма или тип указателя
this не может быть *const, или нужно делать исключение для this из
правила о присваивании указателям со спецификацией const.

R.18.3.4 Приведение указателей на функцию-член


Указатель на функцию-член некоторого объекта можно привести к
указателю на какую-то другую функцию, например (int (*) ())p->f.
Результирующий указатель будет настроен на функцию, вызов которой
будет происходить с помощью обращения к этой функции-члену для
того же объекта. Как обычно результат такого вызова считается
неопределенным.

R.18.3.5 Невложенность классов



Если класс описан внутри другого класса и в программе больше не
описано классов с этим именем, то его можно использовать, как
если бы он был описан вне класса (так обстоит дело с описанием
struct в С), например:

        struct S {
            struct T {
              int a;
            };
            int b;
        };

        struct T x;   // означает `S::T x;'



Список служебных слов


 auto           автоматический
 break          разрыв
 case           вариант
 catch          перехватить
 char           символ
 class          класс
 const          конст
 continue       продолжить
 default        по умолчанию
 delete         удалить
 do             делать
 double         двойной
 else           иначе
 enum           перечисление
 extern         внешний
 float          плавающий
 for            для
 friend         друг
 goto           переход на
 if             если
 inline         подстановка
 int            целый
 long           длинный
 new            новый
 operator       оператор
 private        частный
 protected      защищенный
 public         общий
 register       регистровый
 return         возврат
 short          короткий
 signed         знаковый
 sizeof         размер
 static         статический
 struct         структура
 switch         переключатель
 template       шаблон типа
 this           текущий
 throw          запустить
 try            проверить
 typedef        тип
 union          объединение
 unsigned       беззнаковый
 virtual        виртуальный
 void           пустой
 volatile       изменяемый
 while          пока

УКАЗАТЕЛЬ



        А

абстрактный
абстрактный класс
абстрактный описатель
абстрактный тип данных (АТД)
абстракция
абстракция данных
абстракция данных или наследование
автоматические
агрегат
Ада
аддитивные операции
адрес
адрес битового поля
адрес и присваивание
адрес конструктора
Алгол68
американский национальный институт стандартов (ANSI)
анахронизм
анализатор рекурсивного спуска
арифметика беззнакового
арифметика фиксированной точности
арифметическая особая ситуация
арифметические операции с указателем
арифметические преобразования
арифметический тип
ассемблер
асинхронные события
ассоциативность операций
ассоциативный массив
ассоциативность операций

        Б

базовый
базовый класс
безымянное объединение
беззнаковая арифметика
беззнаковая константа
беззнаковый тип
библиотека
библиотека заголовочных файлов
битовое поле
блок
блокирование (замок)
буферизация ввода-вывода

        В

ввод встроенных типов
ввод и вывод
ввод пользовательских типов
ввод-вывод
виртуальный базовый класс
виртуальный деструктор
виртуальный конструктор
виртуальная функция
включаемый файл
включение исходного файла
внешнее связывание
внутреннее связывание
внутренняя структура
вложенный класс
возврат каретки
возвращаемое функцией значение
восьмеричная константа
восьмеричное число
время жизни объекта
встроенный
встроенная операция
встроенный (основной) тип
выбор члена класса
вывод встроенных типов
вывод пользовательских типов
выделение пробелами
вызов
вызов виртуальной функции
вызов деструктора
вызов операторной функции
вызов по значению
вызов по ссылке
вызов функции
выравнивание
выравнивание битового поля
выравнивание класса
выравнивание члена класса
выражение
выражение-запуска
выражение константа
выражение-константа
выражение-отношения
выражение присваивания
выражение-приведения
выражение-присваивания
выражение-сдвига
выражение-размещения
вычисление стандартного параметра
вычитание указателей

         Г
гибридный проект
глобальная область видимости
глобальная область видимости
глобальное безымянное объединение
глобальное имя
глобальное имя
глобальные данные
глобальные объекты
глубокое копирование
горизонтальная табуляция \t
группирование особых ситуаций

         Д

данные
двойная кавычка
десятичная константа
деструктор
деструктор временного объекта
деструктор локального объекта
деструктор объединения
деструктор производного класса
динамическая инициализация
динамическая информация о типе
динамический контроль типов
динамическая ошибка
длина имени
доступ
доступ к базовому классу
доступ к виртуальной функции
доступ к защищенному члену
доступ к имени члена
доступ к члену базового класса
доступ к члену класса
дружественный класс
дружественная функция
доступ к виртуальной функции

         Е

единица трансляции

        З

завершение программы
заголовочный файл
загрузчик
закрытие потока
запрос ресурса
запуск особой ситуации
запуск программы
зарезервированный идентификатор
защищенный член
знаковый тип

        И
идентификатор
иерархия классов
иерархия особых ситуаций
иерархия объектов
изменяемый адрес
имя
имя класса
имя-класса
имя перегруженного члена
имя перегруженной функции
имя-простого-типа
имя-шаблонного-класса
имя-функции-преобразования
инициализатор
инициализация
инициализация автоматических
инициализация базового класса
инициализация библиотеки
инициализация и присваивание
инициализация массива
инициализация массива объектов класса
инициализация массива символов
инициализация объединения
инициализация объекта класса
инициализация объекта-члена
инициализация регистра
инициализации ссылки
инициализация структуры
инициализация члена
инициализация члена класса
инкапсуляция
интерфейс
интерфейс класса
интерфейсный класс
исходный файл
исчерпание свободной памяти
исчерпание ресурса
исчерпание свободной памяти
итерация

        К
каркас области приложения
класс
класс и тип
класс или объединение
класс или структура
класс особой ситуации
класс памяти auto
Кобол
комментарий
конец строки \n
конкатенация строк
конкретный тип
конкретный тип данных (КТД)
константа
константа double
константа float
константа long
константа long double
константа unsigned
константа перечисления
константа пользовательского типа
константа с плавающей точкой
константа строка
конструктор
конструктор временного объекта
конструктор глобальной переменной
конструктор и абстрактный класс
конструктор копирования
конструктор локального объекта
конструктор локальной переменной
конструктор объединения
конструктор переменной из свободной памяти
конструктор производного класса
конструктор члена класса
конструктор членов массива
контролируемое объединение
контроль диапазона
контроль доступа
контроль типов параметров функции
копирование
косвенность (косвенное обращение)
косвенный базовый класс

        Л

лексема
лексические соглашения
Лисп
литерал
литеральные константы
логическая операция
локальная область видимости

        М

макрокоманда
макрокоманда препроцессора
макрокоманда error
макрокоманда null
макрокоманда pragma
макрообработка
макроопределение
макроподстановка
макроподстановка (подстановка)
манипулятор
массив
метка
метка case
метка default
механизм вызова функции
метод проектирования
многомерный массив
многосимвольная константа
многоуровневая обработка ошибок
множество символов ASCII
множество символов EBCDIC
множественное наследование
модель каскад
модульное программирование
модульность
мультипликативное-выражение

        Н

направленный ацикличный граф
наследование
наследование деструктора
наследование интерфейса
наследование конструктора
неоднозначное преобразование типа
неоднозначность
неоднозначность преобразования класса
неожиданные особые ситуации
неописанный параметр
неперехваченная особая ситуация
неопределенный параметр конструктора
неявный вызов деструктор
неявное преобразование
неявное преобразование типа
неявное пользовательское преобразование

        О

обобщенный пробел
объединение
область видимости
область видимости friend
область видимости вложенного класса
область видимости имени
область видимости класса
область видимости локального класса
область видимости макроимени
область видимости макроопределения
область видимости метки
область видимости функции
область видимости стандартного параметра
обработка ошибок
обработчик особой ситуации
обратная дробная черта \
обратный вызов
обширный интерфейс
общий член класса
объект
объект-функция
объектно-ориентированное программирование
одиночная кавычка
окружение программы
операнд const
операнд volatile
операнд ссылка
оператор
оператор break
оператор continue
оператор do
оператор for
оператор goto
оператор if
оператор return
оператор switch (переключатель)
оператор while
оператор выражения
оператор итерации
оператор описания
оператор перехода
оператор цикла
оператор-выражение
оператор-описание
оператор-перехода
операторная функция (operator)
операционная система UNIX
операция !
операция #
операция ##
операция %=
операция &&
операция &=
операция *=
операция ++
операция +=
операция ,
операция --
операция -=
операция /=
операция ::
операция <<=
операция >>=
операция ^=
операция больше или равно
операция больше чем
операция ввода >>
операция взятия адреса
операция выбора члена класса
операция вывода <<
операция вызова функции
операция декремент
операция запятая
операция индексации
операция инкремент
операция косвенности
операция логического отрицания
операция логическое И
операция логическое ИЛИ
операция меньше или равно
операция меньше чем
операция неравно
операция отношения
операция преобразования
операция приведения
операция равенства
операция присваивания
операция-присваивания
операция разрешения области видимости
операция сдвига влево
операция сложения
операция условия ( ? : )
операция умножения
операция унарный минус
операция delete
операция new
операция sizeof
описание
описание asm
описание extern
описание friend
описание register
описание typedef
описание битового поля
описание в качестве определения
описание внешних
описание вложенного класса
описание доступа
описание дружественного класса
описание или определение
описание имени
описание имени класса
описание класса
описание класса памяти
описание локального класса
описание массива
описание параметра
описание постоянного указателя
описание ссылки
описание статического члена
описание стандартного параметра
описание статического члена
описание типа
описание указателя
описание функции
описание функции-члена
описание члена
описание члена класса
описание шаблона типа
описание-шаблона-типа
описание шаблонного класса
описание шаблонной функции
описание эллипсиса в функции
описание-особой-ситуации
описание-параметра
описатель
описатель-члена
определение
определение виртуальной функции
определение класса
определение конструктора
определение области видимости функции
определение объекта
определение статического члена
определение функции
определение-функции
определение функции-члена
определение функции-члена inline
определение шаблонного класса
определение шаблонной функции
определение чисто виртуальной функции
определение члена
определение шаблонного класса
определение шаблонной функции
определение элемента перечисления
особая ситуация
освобождение ресурса
основной тип
отладка
отличия от вычисления выражений в С
отличия от области видимости С
отличия от описания функции в С
отличия от пространства именования С
отличия от связывания в С
ошибка связывания

        П
память для класса
память для массива
парадигма программирования
параметр
параметры командной строки
параметр функции
парам-шаблона-типа
параметр-шаблона-типа
первичное выражение
перевод формата \f
перегрузка
перегрузка бинарной операции
перегрузка декремента
перегрузка и доступ
перегрузка и область видимости
перегрузка и ссылка
перегрузка имени функции
перегрузка имени члена
перегрузка индексации
перегрузка инкремента
перегрузка операции
перегрузка операции выбора члена
перегрузка операции вызова
перегрузка операции присваивания
перегрузка унарной операции
передача параметра функции
переопределение виртуальной функции
переполнение
перехватить (особую ситуацию)
перечисление
побочные эффекты
поверхностное копирование
повторный запуск (особой ситуации)
поддержка абстракции данных
поддержка объектно-ориентированного программирования
поле
полное-имя-класса
пользовательская операция
пользовательская операция *=
пользовательская операция +
пользовательская операция ++
пользовательская операция -
пользовательская операция --
пользовательская операция ->
пользовательская операция =
пользовательская операция индексации
пользовательский тип
пользовательское преобразование
пользовательская унарная операция
помеченный оператор
поразрядный
поразрядное И
поразрядное включающее ИЛИ
поразрядное исключающее ИЛИ
поразрядное копирование
поразрядные операции
поразрядные логические операции
порядок выполнения операций
порядок вычислений
порядок вычисления выражения
порядок вычисления параметров
последовательность разрядов
постоянное выражение
постфиксное выражение
постфиксные ++ и --
правила разрешения перегрузки
правила областей видимости
правила преобразования типа
предварительное описание
предварительное описание класса
предварительное описание шаблона типа
предопределенная операция взятия адреса
предопределенное присваивание
предопределенные макроимена
преобразование (типа)
преобразование адреса
преобразование класса (типа)
преобразование нулевого указателя
преобразование объекта класса
преобразование одного указателя в другой
преобразование параметра
преобразование плавающей точки в целое
преобразование пользовательского типа
преобразование при присваивании
преобразование пустого указателя
преобразование с помощью конструктора
преобразование ссылки
преобразование типа возвращаемого значения
преобразования типа параметра функции
преобразование указателя
преобразование указателя базового класса
преобразование указателя в класс
преобразование указателя в функцию
преобразование указателя в целое
преобразование указателя в член
преобразование указателя на массив
преобразование указателя на производный класс
преобразование указателя типа void*
преобразование целого
преобразование целого в указатель
префиксные ++ и --
приведение
приведение адреса
приведение базового класса
приведение объекта класса
приведение производного класса
приведение к базовому классу
приведение одного указателя в другой
приведение ссылки
приведение указателя к функции
приведение указателя к целому
приведение указателя к члену
приведение целого к указателю
принадлежность
принадлежность и наследование
приоритет операций
присваивание
присваивание и адрес
присваивание и инициализация
присваивание ссылке
присваивание указателю
проверяемый-блок
проверка типа стандартного параметра
программа
программирование
проектирование С++
проектирование библиотеки
проектирование и классы
проектирование и программирование
проектирование и язык
проектирование сервисных программ
производный класс
прототипы
процедурное программирование
процесс развития
прямой базовый класс
пустая очередь
пустой оператор
пустой список параметров
пустой параметр
пустой указатель (NULL)

         Р
разбиение особых ситуаций
разбиение программы
развитие С++
раздельная трансляция
размер массива по умолчанию
размер строки
размер структуры
размер указателя
размещение
размножение знака
размещение битового поля
разрешение неоднозначности
разрешение перегрузки
разрешение перегрузки шаблонной
разряд (бит)
разрядный вектор
раскручивание стека
расположение битовых полей
расположение объектов класса
реализация
редактор связей
рекурсивный вызов функции
рекурсия

         С
сборка мусора
свободная память
сводка макрокоманд
сводка операторов
сводка операций
сводка правил области видимости
сводка синтаксиса выражения
связывание
связывание внешних
связывание локального имени
связывание статических
связывание шаблона типа
символ
символ шаг назад
символьная константа
символ NULL '\0'
символ подчеркивания _
Симула
синтаксис макроопределений (сводка)
синтаксис операторов
синтаксис выражений
синтаксис макрокоманд
синтаксис операторов
синтаксис описаний
синтаксис описаний класса
синтаксис описателей
синтаксис особых ситуаций
синтаксис шаблона типа
сложное имя класса
служебное слово
служебное слово class
совместимость
совместимость с ANSI C (сводка)
совместимость с С (сводка)
согласованное связывание
сопровождение программ
составной оператор
состояние потока
специальный символ
спецификации интерфейса
спецификация auto
спецификация внешнего связывания
спецификация доступа
спецификация описания
спецификация-базовых
спецификация-класса
спецификация класса памяти
спецификация-особой-ситуации
спецификация-описателя
спецификация-связи
спецификация связи функции
спецификация-типа
спецификация типа double
спецификация-типа-char
спецификация типа enum
спецификация типа float
спецификация типа int
спецификация типа short
спецификация типа struct
спецификация типа union
спецификация типа unsigned
спецификация типа void
спецификация типа volatile
спецификация функции
спецификация шаблона типа
спецификация friend
спецификация inline
спецификация pure
спецификация-pure
спецификация static
спецификация typedef
спецификация template
спецификация virtual
список-базовых
список-выражений
список-инициализаторов
список-обработчиков
список операторных функций
список-описаний
список-описателей
список-парам-шаблона-типа
список-параметров-шаблона-типа
список служебных слов
список-членов
сравнение указателей
средства проектирования
ссылка
ссылка const
ссылка volatile
ссылочное выражение
стадии проектирования
стадии развития
стадии трансляции
стандартные библиотеки
стандартный деструктор
стандартные заголовочные файлы
стандартная инициализация
стандартный каталог include
стандартный каталог включаемых файлов
стандартный компонент
стандартный конструктор
стандартный конструктор копирования
стандартный контроль доступа
стандартная операция присваивания
стандартный параметр
стандартное преобразование
стандартное целочисленное преобразование
статическая функция-член
статический класс памяти
статический контроль типов
статический локальный объект
статический член
статический член класса
строка формата
строковый класс
строковый поток
структура
структура блока

      Т

тело функции
тип
тип битового поля
тип возвращаемого значения
тип виртуальной функции
тип конструктора
тип массива
тип строки
тип указателя
тип функции
тип char
тип double
тип float
тип int
тип long
тип long double
тип short
тип signed char
тип unsigned char
тип void
тип volatile
точка запуска
триграф

       У

узловой класс
указание размещения
указатель
указатель на класс
указатель на функцию
указатель на функцию-член
указатель типа void*
указатель const
унарное выражение
унарная операция
унарная-операция
унарное выражение
унарное-выражение
уничтожение
уничтожение автоматических
уничтожение локальной переменной
уничтожение локальных статических
управление
управление памятью
управление свободной памятью
управляющий класс
управляющая последовательность
управляющий символ (\)
упрятывание имени
уровни абстракции
условная трансляция
условный
уточненное имя
уточненное-имя
уточненное-имя-класса
уточненное-имя-типа

       Ф

файл и поток
файловая область видимости
форматированный вывод
функция
функциональная макрокоманда
функция-подстановка
функция-член
функция-член класса
функция-член friend
функция-член inline (подстановка)
функция-член локального класса
функция-член volatile
функция-член объединения

       Ц

целая константа
цели проектирования
целочисленное преобразование
целочисленный тип
цикл развития

       Ч

частный базовый класс
частный член класса
член
член класса
член производного класса
чисто виртуальная функция

       Ш

шаблон типа
шаблонный класс
шаблонная функция
шаблонная функция-член
шаги проектирования
шестнадцатеричная константа
широкосимвольная строка

      Э

эквивалентность типов
эквивалентность шаблонных типов
элемент перечисления
эллипсис ...

      Я

 явное преобразование типа
 явный вызов деструктора
 явный вызов конструктора
 язык
 язык высокого уровня
 язык низкого уровня
 язык BCPL
 язык C++
 язык CLU
 язык SMALLTALK
 языки C и C++

                            A

 abstract                           абстрактный
    class                           абстрактный класс
 abstract-declarator                абстрактный описатель
 abstraction                        абстракция
    data                            абстракция данных
    levels                          уровни абстракции
 access                             доступ
    base class                      доступ к базовому классу
    base class member               доступ к члену базового класса
    class member                    доступ к члену класса
    control                         контроль доступа
    declaration                     описание доступа
    member name                     доступ к имени члена
    protected member                доступ к защищенному члену
    specifier                       спецификация доступа
    virtual function                доступ к виртуальной функции
 Ada                                Ада
 addition operator                  операция сложения
 additive operators                 аддитивные операции
 address                            адрес
 address-of operator                операция взятия адреса
 ADT abstract type                  абстрактный тип данных (АТД)
 aggregate                          агрегат
 Algol68                            Алгол68
 alignmemt                          выравнивание
 allocation                         размещение
 allocation-expression              выражение-размещения
 ambiguity                          неоднозначность
    resolution                      разрешение неоднозначности
 ambiguous type conversion          неоднозначное преобразование типа
 anachronism                        анахронизм
 anonymous union                    безымянное объединение
 ANSI                               ANSI
 application framework              каркас области приложения
 argument                           параметр
 argument-declaration               описание-параметра
 arithmetic                         арифметический
    conversion                      арифметическое преобразование
    exception                       арифметическая особая ситуация
    fixed point                     арифметика фиксированной точности
    pointer                         арифметические операции с указателем
    type                            арифметический тип
    unsigned                        арифметика беззнакового
 array                              массив
    associative                     ассоциативный массив
    initialization                  инициализация массива
    multidimensional                многомерный массив
    storage of                      память для массива
    type                            тип массива
 arrow operator                     выбор члена класса
 ASCII character set                множество символов ASCII
 asm declaration                    описание asm
 assembler                          ассемблер
 assignment                         присваивание
    and initialization              присваивание и инициализация
    and lvalue                      присваивание и адрес
 assignment-expression              выражение-присваивания
 assignment-operator                операция-присваивания
 associativity of operator          ассоциативность операций
 asynchronous events                асинхронные события
 auto                               автоматические
   destruction                      уничтожение автоматических
   initialization                   инициализация автоматических
   specifier                        спецификация auto
   storage class                    класс памяти auto

                           B

 backslash                          обратная дробная черта
 backspace                          символ шаг назад
 base                               базовый
   class                            базовый класс
   class access                     доступ к базовому классу
   class cast                       приведение к базовому классу
 base-list                          список-базовых
 base-specifier                     спецификация-базовых
 BCPL                               BCPL
 binding                            связывание
 bit                                разряд (бит)
   field                            битовое поле
   pattern                          последовательность разрядов
   vector                           разрядный вектор
 bit-field                          битовое поле
   address of                       адрес битового поля
   alignment                        выравнивание битового поля
   declaration                      описание битового поля
   layout                           размещение битового поля
   type                             тип битового поля
 bitwise                            поразрядный
   AND operator                     поразрядное И
   copy                             поразрядное копирование
   exclusive OR operator            поразрядное исключающее ИЛИ
   inclusive OR operator            поразрядное включающее ИЛИ
   logical operators                логические операции
   operators                        поразрядные операции
 block                              блок
   statement { }                    составной оператор
   structure                        структура блока
 body, function                     тело функции
 break statement                    оператор break
 buffering, I/O                     буферизация ввода-вывода
 built-in                           встроенный
   operator                         встроенная операция
   type                             встроенный (основной) тип

                         C

 C                                  C
 C ANSI                             C ANSI
   C++                              C и C++
 C++                                C++
    evolution                       развитие C++
 call                               вызов
    by reference                    вызов по ссылке
    by value                        вызов по значению
    function                        вызов функции
    operator function               вызов операторной функции
 callback                           обратный вызов
 carriage return                    возврат каретки
 cast                               приведение
    base class                      приведение  базового класса
    class object                    приведение  объекта класса
    derived class                   приведение  производного класса
    integer to pointer              приведение целого к указателю
    operator                        операция приведения
    pointer to function             приведение указателя к функции
    pointer to integer              приведение указателя к целому
    pointer to member               приведение указателя к члену
    pointer to pointer              приведение одного указателя
                                    в другой
    reference                       приведение ссылки
 cast-expression                    выражение-приведения
 catch                              перехватить
 CDT concrete type                  конкретный тип данных (КТД)
 char type                          тип char
    type, signed                    тип signed char
    type specifier                  спецификация-типа-char
    type, unsigned                  тип unsigned char
 character                          символ
    constant                        символьная константа
    set, ASCII                      множество символов ASCII
    set, EBCDIC                     множество символов EBCDIC
 class                              класс
    abstract                        абстрактный класс
    alignment                       выравнивание класса
    and type                        класс и тип
    base                            базовый класс
    constructor and abstract        конструктор и абстрактный класс
    constructor for derived         конструктор для производного класса
    conversion                      преобразование объекта класса
    conversion ambiguity            неоднозначность преобразования
                                    объекта класса
    declaration                     описание класса
    declaration, forward            предварительное описание класса
    declaration, friend             описание дружественного класса
    definition                      определение класса
    derived                         производный класс
    destructor for derived          деструктор производного класса
    exception                       класс особой ситуации
    friend                          дружественный класс
    handle                          управляющий класс
    hierarchy                       иерархия классов
    interface                       интерфейс класса
    member                          член класса
    member access                   доступ к члену класса
    member access operator          операция выбора члена класса
    member, alignment               выравнивание члена класса
    member, constructor for         конструктор члена класса
    member declaration              описание члена класса
    member function                 функция-член класса
    member initialization           инициализация члена класса
    member of derived               член производного класса
    member, private                 частный член класса
    member, public                  общий член класса
    member, static                  статический член класса
    name                            имя класса
    name declaration                описание имени класса
    name, elaborated                сложное имя класса
    nested                          вложенный класс
    node                            узловой класс
    pointer to                      указатель на класс
    private base                    частный базовый класс
    scope                           область видимости класса
    storage                         память для класса
    template                        шаблонный класс
    versus struct                   класс или структура
    versus union                    класс или объединение
    virtual base                    виртуальный базовый класс
 class-key                          служебное слово class
 class-name                         имя-класса
 class-specifier                    спецификация-класса
 CLU                                CLU
 Cobol                              Кобол
 comma operator                     операция запятая
 command line argument              параметры командной строки
 comment                            комментарий
 compatibility                      совместимость
     with ANSI C summary            совместимость с ANSI C (сводка)
     with C summary                 совместимость с С (сводка)
 compilation, separate              раздельная трансляция
 complete-class-name                полное-имя-класса
 compound statement                 составной оператор
 concatenation string               конкатенация строк
 concrete type                      конкретный тип
     type, CDT                      конкретный тип данных (КТД)
 conditional                        условный
     compilation                    условная трансляция
     expression operator            операция условия ( ? : )
 constant                           константа
     character                      символьная константа
     decimal                        десятичная константа
     double                         константа double
     enumeration                    константа перечисления
     expression                     выражение константа
     float                          константа float
     floating point                 константа с плавающей точкой
     hexadecimal                    шестнадцатеричная константа
     integer                        целая константа
     long                           константа long
     long double                    константа long double
     multicharacter                 многосимвольная константа
     octal                          восьмеричная константа
     of user-defined type           константа пользовательского типа
     unsigned                       константа unsigned
 constant-expression                выражение-константа
 constructor                        конструктор
     address of                     адрес конструктора
     call, explicit                 явный вызов конструктора
     conversion by                  преобразование с помощью
                                    конструктора
     copy                           конструктор копирования
     default                        стандартный конструктор
     default copy                   стандартный конструктор копирования
     definition                     определение конструктора
     exception handling             конструктор в обработке
                                    особых ситуаций
     for array members              конструктор членов массива
     for class member               конструктор члена класса
     for derived class              конструктор производного класса
     for free store variable        конструктор переменной,
                                    размещаемой в свободной памяти
     for global variable            конструктор глобальной переменной
     for local variable             конструктор локальной переменной
     for temporary                  конструктор временного объекта
     inheritance                    наследование конструкторов
     local object                   конструктор локального объекта
     type of                        тип конструктора
     undefined argument to          неопределенный параметр конструктора
     union                          конструктор объединения
     virtual                        виртуальный конструктор
 containment                        принадлежность
     and inheritance                принадлежность и наследование
 continue statement                 оператор continue
 control access                     контроль доступа
 conversion                         преобразование (типа)
     argument                       преобразование параметра
     arithmetic                     арифметические преобразования
     array pointer                  преобразование указателя на массив
     base class pointer             преобразование указателя
                                    базового класса
     by assignment                  преобразование при присваивании
     by constructor                 преобразование конструктором
     class                          преобразование класса (типа)
     derived class pointer          преобразование указателя
                                    на производного класса
     floating point integer         преобразование значения с
                                    плавающей точкой в целое
     implicit                       неявное преобразование
     implicit type                  неявное преобразование типа
     integer                        преобразование целого
     integer to pointer             преобразование целого в указатель
     lvalue                         преобразование адреса
     null pointer                   преобразование пустого указателя
     of pointer to class            преобразование указателя в класс
     operator                       операция преобразования
     pointer                        преобразование указателя
     pointer to function            преобразование указателя в функцию
     pointer to integer             преобразование указателя в целое
     pointer to member              преобразование указателя в член
     pointer to pointer             преобразование одного указателя
                                    в другой
     reference                      преобразование ссылки
     return type                    преобразование типа возвращаемого
                                    значения
     rules, type                    правила преобразования типа
     standard                       стандартное преобразование
     user-defined                   пользовательское преобразование
     user-defined type              преобразование пользовательского типа
     void* pointer                  преобразование указателя типа void*
     zero pointer                   преобразование нулевого указателя
 conversion-function-name           имя-функции-преобразования
 copy                               копирование
     bitwise                        поразрядное копирование
     deep                           глубокое копирование
     shallow                        поверхностное копирование

                               D

 data                               данные
     abstraction                    абстракция данных
     abstraction, support for       поддержка абстракции данных
     abstraction vs inheritance     абстракция данных или наследование
     global                         глобальные данные
 debugging                          отладка
 declaration                        описание
     access                         описание доступа
     argument                       описание параметра
     array                          описание массива
     as definition                  описание в качестве определения
     asm                            описание asm
     bit-field                      описание битового поля
     class                          описание класса
     class member                   описание члена класса
     class name                     описание имени класса
     constant pointer               описание постоянного указателя
     default argument               описание стандартного параметра
     definition versus              описание или определение
     ellipsis in function           описание эллипсиса в функции
     extern                         описание extern
     forward                        предварительное описание
     friend                         описание friend
     friend class                   описание дружественного класса
     function                       описание функции
     function member                описание функции-члена
     function template              описание шаблонной функции
     local class                    описание локального класса
     member                         описание члена
     name                           описание имени
     pointer                        описание указателя
     reference                      описание ссылки
     register                       описание register
     specifier                      спецификация описания
     statement                      оператор описания
     static member                  описание статического члена
     storage class                  описание класса памяти
     syntax summary                 синтаксис описаний (сводка)
     syntax summary, class          синтаксис описаний класса (сводка)
     template                       описание шаблона типа
     template class                 описание шаблонного класса
     template function              описание шаблонной функции
     type                           описание типа
     typedef                        описание typedef
 declaration                        описание
 declaration-list                   список-описаний
 declaration-statement              оператор-описание
 declarator                         описатель
 declarator-list                    список-описателей
 decl-specifier                     спецификация-описателя
 decrement operator                 операция декремент
 default access control             стандартный контроль доступа
      argument                      стандартный  параметр
      argument evaluation           вычисление стандартного параметра
      argument, scope               область видимости стандартного параметра
      argument type checking        проверка типа стандартного параметра
      array size                    размер массива по умолчанию
      assignment operator           стандартная операция присваивания
      constructor                   стандартный конструктор
      copy constructor              стандартный конструктор копирования
      destructor                    стандартный деструктор
      initialization                стандартная инициализация
 definition                         определение
      class                         определение класса
      constructor                   определение конструктора
      enumerator                    определение элемента перечисления
      function                      определение функции
      function template             определение шаблонной функции
      inline member function        определение функции-члена inline
      member                        определение члена
      member function               определение функции-члена
      object                        определение объекта
      pure virtual function         определение чисто виртуальной
                                    функции
      scope of function             определение области видимости
                                    функции
      static member                 определение статического члена
      template class                определение шаблонного класса
      template function             определение шаблонной функции
      virtual function              определение виртуальной функции
 delete operator                    операция delete
 dereferencing                      косвенность (косвенное обращение)
 derived class                      производный класс
 design aims                        цели проектирования
      and classes                   проектирование и классы
      and language                  проектирование и язык
      and programming               проектирование и программирование
      library                       проектирование библиотеки
      method                        метод проектирования
      of C++                        проектирование С++
      stage                         стадии проектирования
      steps                         шаги проектирования
      tools                         средства проектирования
 destruction                        уничтожение
      of auto                       уничтожение автоматических
      of local static               уничтожение локальных статических
      of local variable             уничтожение локальной переменной
 destructor                         деструктор
      default                       стандартный деструктор
      for derived class             деструктор производного класса
      for temporary                 деструктор временного объекта
      inheritance                   наследование деструктора
      invocation                    вызов деструктора
      local object                  деструктор локального объекта
      virtual                       виртуальный деструктор
 development cycle                  цикл развития
      process                       процесс развития
      stages                        стадии развития
 difference from C expression       отличия от вычисления выражений в С
 evaluation
     from C function declaration    отличия от описания функции в С
     from C linkage                 отличия от связывания в С
     from C name space              отличия от пространства именования С
     from C scope                   отличия от области видимости С
 direct base class                  прямой базовый класс
 directed acyclic graph             направленный ацикличный граф
 directive error preprocessing      макрокоманда error
     pragma preprocessing           макрокоманда pragma
     preprocessing                  макрокоманда препроцессора
 discriminating union               контролируемое объединение
 discrimination of exceptions       разбиение особых ситуаций
 do statement                       оператор do
 double constant                    константа double
     type                           тип double
     type specifier                 спецификация типа double
 dynamic type checking              динамический контроль типов

                            E

 EBCDIC character set               множество символов EBCDIC
 elaborated class name              сложное имя класса
 ellipsis ...                       эллипсис ...
 empty argument list                пустой список параметров
     queue                          пустая очередь
     statement                      пустой оператор
 encapsulation                      инкапсуляция
 enum type specifier                спецификация типа enum
 enumeration                        перечисление
     constant                       константа перечисления
 enumerator                         элемент перечисления
 equality operator                  операция равенства
 equivalence template type          эквивалентность шаблонных типов
     type                           эквивалентность типов
 error handling                     обработка ошибок
     handling, multilevel           многоуровневая обработка ошибок
     linkage                        ошибка связывания
     preprocessing directive        макрокоманда error
     run-time                       динамическая ошибка
 escape character                   управляющий символ (\)
     sequence                       управляющая последовательность
 evaluation default argument        вычисление стандартного параметра
     of expression, order of        порядок вычисления выражения
     order of                       порядок вычислений
     order of argument              порядок вычисления параметров
 evolution of С++                   развитие С++
 exception                          особая ситуация
     arithmetic                     арифметическая особая ситуация
     class                          класс особой ситуации
     handler                        обработчик особой ситуации
     hierarchies                    иерархия особых ситуаций
     throwing                       запуск особой ситуации
 exception-declaration              описание-особой-ситуации
 exception-specification            спецификация-особой-ситуации
 exhaustion free store              исчерпание свободной памяти
 explicit constructor call          явный вызов конструктора
     destructor call                явный вызов деструктора
     type conversion                явное преобразование типа
 expression                         выражение
     assignment                     выражение присваивания
     constant                       постоянное выражение
     order of evaluation of         порядок вычисления выражения
     postfix                        постфиксное выражение
     primary                        первичное выражение
     reference                      ссылочное выражение
     statement                      оператор выражение
     syntax summary                 сводка синтаксиса выражения
     unary                          унарное выражение
 expression-list                    список-выражений
 expression-statement               оператор-выражение
 extern declaration                 описание внешних
     linkage                        связывание внешних
     linkage specification          спецификация внешнего связывания
 external linkage                   внешнее связывание

                             F

 fat interface                       обширный интерфейс
 field                               поле
    bit                              битовое поле
 fixed point arithmetic              арифметика с фиксированной точностью
 float constant                      константа float
    type                             тип float
    type specifier                   спецификация типа float
 for statement                       оператор for
 format string                       строка формата
 form feed \f                        перевод формата \f
 forward class declaration           предварительное описание класса
    declaration                      предварительное описание
    declaration of template          предварительное описание шаблона типа
 free store                          свободная память
    store exhaustion                 исчерпание свободной памяти
    store management                 управление свободной памятью
 friend                              friend
    class                            дружественный класс
    declaration                      описание friend
    function                         дружественная функция
    member function                  функция-член friend
    scope of                         область видимости friend
    specifier                        спецификация friend
 function                            функция
    argument                         параметр функции
    argument passing                 передача параметра функции
    argument type checking           контроль типов параметров функции
    argument type conversion         преобразования типа параметра
                                     функции
    body                             тело функции
    call                             вызов функции
    call mechanism                   механизм вызова функции
    call, recursive                  рекурсивный вызов функции
    declaration                      описание функции
    definition                       определение функции
    friend                           дружественная функция
    inline                           функция-подстановка
    inline member                    функция-член inline (подстановка)
    linkage specification            спецификация связи функции
    member                           функция-член
    object                           объект-функция
    operator                         операторная функция (operator)
    pointer to                       указатель на функцию
    pointer to member                указатель на функцию-член
    pure virtual                     чисто виртуальная функция
    scope                            область видимости функции
    specifier                        спецификация функции
    template                         шаблонная функция
    type                             тип функции
    value return                     возвращаемое функцией значение
    virtual                          виртуальная функция
 function-definition                 определение-функции
 fundamental type                    основной тип

                             G

 garbage collection                  сборка мусора
 global anonymous union              глобальное безымянное объединение
    data                             глобальные данные
    name                             глобальное имя
    objects                          глобальные объекты
    scope                            глобальная область видимости
 goto statement                      оператор goto
 greater than operator               операция больше чем
 greater than or equal operator      операция больше или равно
 grouping of exceptions              группирование особых ситуаций

                            H
 handle class                        управляющий класс
 handler, exception                  обработчик особой ситуации
 handler-list                        список-обработчиков
 header file                         заголовочный файл
 hexadecimal constant                шестнадцатеричная константа
 hierarchy class                     иерархия классов
    object                           иерархия объектов
 horizontal tab \t                   горизонтальная табуляция \t
 hybrid design                       гибридный проект

                            I

 identifier                          идентификатор
 if statement                        оператор if
 implementation                      реализация
 implicit conversion                 неявное преобразование
    destructor call                  неявный вызов деструктор
    type conversion                  неявное преобразование типа
    user-defined conversion          неявное пользовательское
                                     преобразование
 include directory, standard         стандартный каталог include
    file                             включаемый файл
 inclusion source file               включение исходного файла
 increment operator                  операция инкремент
 indentation                         выделение пробелами
 inderect base class                 косвенный базовый класс
 inderection operator                операция косвенности
 inequality operator                 операция неравно
 inheritance                         наследование
    containment and                  принадлежность и наследование
    multiple                         множественное наследование
    of constructor                   наследование конструктора
    of destructor                    наследование деструктора
 initialization                      инициализация
    array                            инициализация массива
    array of class objects           инициализация массива объектов класса
    assignment and                   инициализация и присваивание
    character array                  инициализация массива символов
    class member                     инициализация члена класса
    class object                     инициализация объекта класса
    default                          стандартная инициализация
    dynamic                          динамическая инициализация
    member                           инициализация члена
    member object                    инициализация объекта-члена
    of base class                    инициализация базового класса
    of structure                     инициализация структуры
 initializer                         инициализатор
 initializer-list                    список-инициализаторов
 inline                              inline
    function                         функция-подстановка
    member function                  функция-член inline
 input and output                    ввод-вывод
    of built-in type                 ввод встроенных типов
    of user-defined type             ввод пользовательских типов
    operator >>                      операция ввода >>
 int                                 int
   type                              тип int
   type specifier                    спецификация типа int
 integer constant                    целая константа
   conversion                        целочисленное преобразование
 integral promotion                  стандартное целочисленное
                                     преобразование
   type                              целочисленный тип
 interface                           интерфейс
   class                             интерфейсный класс
   inheritance                       наследование интерфейса
   fat                               обширный интерфейс
   specifications                    спецификации интерфейса
 internal linkage                    внутреннее связывание
   structure                         внутренняя структура
 I/O buffering                       буферизация ввода-вывода
 iteration                           итерация
   statement                         оператор итерации

                          J

 jump statement                       оператор перехода
 jump-statement                       оператор-перехода

                          K

 keyword                              служебное слово
    list                              список служебных слов

                          L

 label                                 метка
    case                               метка case
    default                            метка default
    scope of                           область видимости метки
 labeled statement                     помеченный оператор
 language                              язык
    design and                         проектирование и язык
    high-level                         язык высокого уровня
    low-level                          язык низкого уровня
 layout bit-field                      расположение битовых полей
    class objects                      расположение объектов класса
 left shift operator                   операция сдвига влево
 less than operator                    операция меньше чем
    than or equal to operator          операция меньше или равно
 levels of abstraction                 уровни абстракции
 lexical conventions                   лексические соглашения
 library                               библиотека
    design                             проектирование библиотеки
    headers                            библиотека заголовочных файлов
    initialization                     инициализация библиотеки
 lifetime of object                    время жизни объекта
 linkage                               связывание
    consistency                        согласованное связывание
    error                              ошибка связывания
    external                           внешнее связывание
    internal                           внутреннее связывание
 linker-specification                  спецификация-связи
 linker                                редактор связей
 Lisp                                  Лисп
 list of operator functions            список операторных функций
 literal                               литерал
    constants                          литеральные константы
 loader                                загрузчик
 local class declaration               описание локального класса
    class member function              функция-член локального класса
    class, scope of                    область видимости локального класса
    scope                              локальная область видимости
 locking                               блокирование (замок)
 logical AND operator                  операция логическое И
    OR operator                        операция логическое ИЛИ
    negation operator                  операция логического отрицания
    operators, bitwise                 поразрядные логические операции
 long                                  long
    constant                           константа long
    double                             long double
    double constant                    константа long double
    double type                        тип long double
    type                               тип long
 loop statement                        оператор цикла
 lvalue                                адрес
    assignment and                     адрес и присваивание
    cast                               приведение адреса
    conversion                         преобразование адреса
    modifiable                         изменяемый адрес

                            M

 macro                                 макрокоманда
    definition, preprocessing          макроопределение
    expansion, preprocessing           макроподстановка
    function-like                      функциональная макрокоманда
    name, scope of                     область видимости макроопределения
    names, predefined                  предопределенные макроимена
    preprocessing                      макрообработка
    syntax summary                     синтаксис макроопределений (сводка)
 maintenance, software                 сопровождение программ
 management                            управление
    free store                         управление свободной памятью
    memory                             управление памятью
 manipulator                           манипулятор
 member                                член
 member-declaration                    описание-члена
 member-declarator                     описатель-члена
 member-list                           список-членов
 modifiable lvalue                     изменяемый адрес
 modular programming                   модульное программирование
 modularity                            модульность
 multicharacter constant               многосимвольная константа
 multidimensional array                многомерный массив
 multiple inheritance                  множественное наследование
 multiplication operator               операция умножения
 multiplicative-expression             мультипликативное-выражение

                               N

 name                                  имя
    global                             глобальное имя
    hiding                             упрятывание имени
    length of                          длина имени
    linkage of local                   связывание локального имени
    overloaded function                имя перегруженной функции
    overloaded member                  имя перегруженного члена
    qualified                          уточненное имя
    scope of                           область видимости имени
 nested class declaration              описание вложенного класса
    class, scope of                    область видимости вложенного класса
 new operator                          операция new
 newline \n                            конец строки \n
 node class                            узловой класс
 null character                        символ null '\0'
    pointer                            пустой указатель (null)

                              O

 object                                объект
 object-oriented programming           объектно-ориентированное
                                       программирование
 octal constant                        восьмеричная константа
    number                             восьмеричное число
 operand const                         операнд const
    reference                          операнд ссылка
    volatile                           операнд volatile
 operator ,                            операция ,
    !                                  операция !
    #                                  операция #
    ##                                 операция ##
    %=                                 операция %=
    &&                                 операция &&
    &=                                 операция &=
    *=                                 операция *=
    *=, user-defined                   пользовательская операция *=
    +, user-defined                    пользовательская операция +
    ++                                 операция ++
    ++, user-defined                   пользовательская операция ++
    +=                                 операция +=
    -, user-defined                    пользовательская операция -
    --                                 операция --
    --, user-defined                   пользовательская операция --
    -=                                 операция -=
    ->, user-defined                   пользовательская операция ->
    /=                                 операция /=
    ::                                 операция ::
    <<, output                         операция вывода <<
    <<=                                операция <<=
    =, user-defined                    пользовательская операция =
    >>, input                          операция ввода >>
    >>=                                операция >>=
    ^=                                 операция ^=
    address-of                         операция взятия адреса
    assignment                         операция присваивания
    associativity                      ассоциативность операций
    binding strength                   порядок выполнения операций
    built-in                           встроенные операции
    function call                      операция вызова функции
    precedence                         приоритет операций
    sizeof                             операция sizeof
    subscripting                       операция индексации
    summary                            сводка операций
    user-defined                       пользовательская операция
 operator function                     операторная функция
    function, list of                  список операторных функций
 order of argument evaluation          порядок вычисления параметров
    of evaluation                      порядок вычислений
 output formatted                      форматированный вывод
    input and                          ввод и вывод
    of built-in type                   вывод встроенных типов
    of user-defined type               вывод пользовательских типов
    operator <<                        операция вывода <<
 overflow                              переполнение
 overloaded assignment operator        перегрузка операции присваивания
    binary operator                    перегрузка бинарной операции
    decrement operator                 перегрузка декремента
    function call operator             перегрузка операции вызова
    function name                      перегрузка имени функции
    increment operator                 перегрузка инкремента
    member access operator             перегрузка операции выбора члена
    member name                        перегрузка имени члена
    operator                           перегрузка операции
    subscripting operator              перегрузка индексации
    unary operator                     перегрузка унарной операции
 overloading                           перегрузка
    and access                         перегрузка и доступ
    and scope                          перегрузка и область видимости
    resolution                         разрешение перегрузки
    resolution rules                   правила разрешения перегрузки
 overriding virtual function           переопределение виртуальной
                                       функции

                                P

 paradigm, programming                 парадигма программирования
 placement                             указание размещения
 pointer                               указатель
    arithmetic                         арифметические операции указателей
    assignment to                      присваивание указателю
    comparison                         сравнение указателей
    const                              указатель const
    conversion                         преобразование указателей
    declaration                        описание указателя
    null                               пустой указатель null
    size of                            размер указателя
    substraction                       вычитание указателей
    type                               тип указателя
 postfix ++ and --                     постфиксные ++ и --
    expression                         постфиксное выражение
 precedence of operator                приоритет операций
 predefined address-of operator        предопределенная операция
                                       взятия адреса
    assignment operator                предопределенное присваивание
    macronames                         предопределенные макроимена
 prefix ++ and --                      префиксные ++ и --
 preprocessing                         макрообработка
    directive                          макрокоманда
    directive, error                   макрокоманда error
    directive, null                    макрокоманда null
    directive, pragma                  макрокоманда pragma
    macro definition                   макроопределение
    macro expansion                    макроподстановка (подстановка)
    syntax summary                     сводка макрокоманд
 primary expression                    первичное выражение
 private                               private
    base class                         частный базовый класс
    class member                       частный член класса
 procedural programming                процедурное программирование
 program                               программа
    environment                        окружение программы
    partitioning                       разбиение программы
    start                              запуск программы
    termination                        завершение программы
 protected                             protected
    member                             защищенный член
    member access                      доступ к защищенному члену
 prototypes                            прототипы
 public                                public
    class member                       общий член класса
 pure specifier                        спецификация pure
    virtual function                   чисто виртуальная функция
 pure-specifier                        спецификация-pure

                          Q

 qualified name                        уточненное имя
 qualified-class-name                  уточненное-имя-класса
 qualified-name                        уточненное-имя
 qualified-type-name                   уточненное-имя-типа
 queue empty                           пустая очередь
 quote, single                         одиночная кавычка
    double                             двойная кавычка

                           R

 range checking                        контроль диапазона
 recursion                             рекурсия
 recursive decent parser               анализатор рекурсивного спуска
    function call                      рекурсивный вызов функции
 reference                             ссылка
    assignment                         присваивание ссылки
    assignment to                      присваивание ссылке
    call by                            вызов по ссылке
    cast                               приведение ссылки
    conversion                         преобразование ссылки
    const                              ссылка const
    declaration                        описание ссылки
    initialization                     инициализации ссылки
    operand                            операнд ссылка
    overloading and                    перегрузка и ссылка
    volatile                           ссылка volatile
 register declaration                  описание register
    initialization                     инициализация регистра
 relational operator                   операция отношения
 relational-expression                 выражение-отношения
 reserved identifier                   зарезервированный идентификатор
 resolution ambiguity                  разрешение неоднозначности
    scoping ambiguity                  разрешение неоднозначности
                                       области видимости
    template function overloading      разрешение перегрузки шаблонной
                                       функции
 resource acquisition                  запрос ресурса
    exhaustion                         исчерпание ресурса
    release                            освобождение ресурса
 re-throw                              повторный запуск (особой ситуации)
 return                                return
 return statement                      оператор return
 return type                           тип возвращаемого значения
 run-time error                        динамическая ошибка
    initialization                     динамическая инициализация
    type information                   динамическая информация о типе

                               S

 scope                                 область видимости
    class                              область видимости класса
    file                               файловая область видимости
    function                           область видимости функции
    global                             глобальная область видимости
    local                              локальная область видимости
    of label                           область видимости метки
    of local class                     область видимости локального класса
    of macro name                      область видимости макроимени
    of name                            область видимости имени
    of nested class                    область видимости вложенного класса
    resolution operator                операция разрешения области
                                       видимости
    rules summary                      сводка правил области видимости
 separate compilation                  раздельная трансляция
 shift-expression                      выражение-сдвига
 short type                            тип short
    type specifier                     спецификация типа short
 side effects                          побочные эффекты
 sign extension                        размножение знака
 signed char type                      тип signed char
    type                               знаковый тип
 simple-type-name                      имя-простого-типа
 Simula                                Симула
 size of pointer                       размер указателя
    of string                          размер строки
    of structure                       размер структуры
 sizeof operator                       операция sizeof
 Smalltalk                             Smalltalk
 source file                           исходный файл
    file, inclusion                    включение исходного файла
 special character                     специальный символ
 specifier auto                        спецификация auto
    declaration                        спецификация описания
    friend                             спецификация friend
    function                           спецификация функции
    inline                             спецификация inline
    static                             спецификация static
    storage class                      спецификация класса памяти
    template                           спецификация шаблона типа
    typedef                            спецификация typedef
    virtual                            спецификация virtual
 stack unwinding                       раскручивание стека
 standard component                    стандартный компонент
    conversion                         стандартное преобразование
    headers                            стандартные заголовочные файлы
    include directory                  стандартный каталог включаемых
                                       файлов
    libraries                          стандартные библиотеки
 statement                             оператор
    break                              оператор break
    compound                           составной оператор
    continue                           оператор continue
    declaration                        оператор описания
    do                                 оператор do
    empty                              пустой оператор
    expression                         оператор выражения
    for                                оператор for
    goto                               оператор goto
    if                                 оператор if
    summary                            сводка операторов
    switch                             оператор switch (переключатель)
    syntax summary                     синтаксис операторов
    while                              оператор while
 static type checking                  статический контроль типов
 static                                static
    class member                       статический член класса
    linkage of                         связывание статических
    local object                       статический локальный объект
    member                             статический член
    member declaration                 описание статического члена
    member definition                  определение статического члена
    member function                    статическая функция-член
    specifier                          спецификация static
    storage class                      статический класс памяти
 stream closing of                     закрытие потока
    file and                           файл и поток
    state                              состояние потока
    string                             строковый поток
 string class                          строковый класс
    concatenation                      конкатенация строк
    constant                           константа строка
    type of                            тип строки
    wide-character                     широкосимвольная строка
 struct                                struct
    type specifier                     спецификация типа struct
 structure                             структура
    initialization of                  инициализация структуры
 subclass                              вложенный класс
 subscripting user-defined             пользовательская операция индексации
 summary class declaration syntax      синтаксис описаний класса
    compatibility with ANSI C          совместимость с ANSI C
    compatibility with C               совместимость с С
    declaration syntax                 синтаксис описаний
    declarator syntax                  синтаксис описателей
    exception handling syntax          синтаксис особых ситуаций
    expression syntax                  синтаксис выражений
    macro syntax                       синтаксис макрокоманд
    scope rules                        правила областей видимости
    statement syntax                   синтаксис операторов
    template syntax                    синтаксис шаблонов типа
 support for data abstraction          поддержка абстракции данных
 for object-oriented programming       поддержка объектно-ориентированного
                                       программирования

                                 T

 template                              шаблон типа
    class                              шаблонный класс
    class declaration                  описание шаблонного класса
    class definition                   определение шаблонного класса
    declaration                        описание шаблона типа
    function                           шаблонная функция
    function declaration               описание шаблонной функции
    function definition                определение шаблонной функции
    linkage of                         связывание шаблона типа
    member function                    шаблонная функция-член
    specifier                          спецификация template
    syntax summary                     синтаксис шаблона типа
 template-arg                          парам-шаблона-типа
 template-arg-list                     список-парам-шаблона-типа
 template-argument                     параметр-шаблона-типа
 template-argument-list                список-параметров-шаблона-типа
 template-class-name                   имя-шаблонного-класса
 template-declaration                  описание-шаблона-типа
 temporary                             временный объект
 this                                  this
 throw                                 throw
 throw-expression                      выражение-запуска
 throwing, exception                   запуск особой ситуации
 throw-point                           точка запуска
 token                                 лексема
 tools design                          проектирование сервисных программ
 translation phases                    стадии трансляции
    unit                               единица трансляции
 trigraph                              триграф
 try                                   try
 try-block                             проверяемый-блок
 type                                  тип
    user-defined                       пользовательский тип
 type-specifier                        спецификация-типа

                             U

 unary expression                      унарное выражение
     minus operator                    операция унарный минус
     operator                          унарная операция
     operator, user-defined            пользовательская унарная операция
     plus, operator                    операция унарный плюс
 unary-expression                      унарное-выражение
 unary-operator                        унарная-операция
 uncaught exception                    неперехваченная особая ситуация
 undeclared argument                   неописанный параметр
 underscore character                  символ подчеркивания _
 unexpected exceptions                 неожиданные особые ситуации
 union                                 объединение
    anonymous                          безымянное объединение
    constructor                        конструктор объединения
    destructor                         деструктор объединения
    discriminating                     контролируемое объединение
    initialization                     инициализация объединения
    member function                    функция-член объединения
    type specifier                     спецификация типа union
 UNIX                                  UNIX
 unsigned arithmetic                   беззнаковая арифметика
    char type                          тип unsigned char
    constant                           беззнаковая константа
    type                               беззнаковый тип
    type specifier                     спецификация типа unsigned

                            V

 vertical tab \v                       вертикальная табуляция \v
 virtual                               virtual
    base class                         виртуальный базовый класс
    destructor                         виртуальный деструктор
    function                           виртуальная функция
    function access                    доступ к виртуальной функции
    function call                      вызов виртуальной функции
    function, type of                  тип виртуальной функции
    specifier                          спецификация virtual
    user-defined conversion            виртуальное пользовательское
                                       преобразование
 void                                  void
    argument                           пустой параметр
    pointer to                         указатель типа void*
    type                               тип void
    type specifier                     спецификация типа void
 volatile                              volatile
    member function                    функция-член volatile
    operand                            операнд volatile
    reference                          ссылка volatile
    type                               тип volatile
    type specifier                     спецификация типа volatile

                              W

 waterfall model                       модель каскад
 white space                           обобщенный пробел
 wide-character string                 широкосимвольная строка


 * ПРИМЕРЫ * 



b1_1_1.cxx

#include <stream.hxx>

main()
{
    cout << "Hello, world\n";
}



b1_1_3.cxx

#include <stream.hxx>

main ()
{
 int inch = 0;
 cout << "inches=";
 cin  >> inch;
 cout << inch;
 cout << "in = ";
 cout << inch*2.54;
 cout << " cm\n";
}



b1_4_5v.cxx

#include <stream.hxx>

main()
{
 const float fac = 2.54;
 float x, in, cm;
 char ch = 0;

for ( int i= 0; i< 8; i++) {
 cerr << "enter length: ";
 cin >> x >> ch;

 if (ch == 'i' ) {   // inch
    in = x;
    cm = x*fac;
 }
 else if (ch == 'c') { // cm
     in = x/fac;
     cm = x;
 }
 else
    in = cm = 0;

 cerr << in << "in = " << cm << " cm\n";
}
}



b1_5.cxx

#include <stream.hxx>
extern float pow(float, int);

main()
{
 for (int i=0; i<10; i++) cout << pow(2,i) << "\n";
}

extern void error(char *);

float pow(float x, int n)
{
  if (n < 0)  {
     error ("sorry, negative exponent to pow()");
     return 0;
     }

  switch (n) {
  case 0:   return 1;
  case 1:   return x;
  default:  return x*pow(x,n-1);
  }
}

void error(char *s)
{
 cout << s;
}



b1__13.cxx

#include <stream.hxx>

// 1.11
class vector {
  int *v;
  int sz;
public:
       vector(int);   // constructor
       ~vector();    // destructor
  int size() { return sz; }
  void set_size(int);
  int& operator[](int);
  int& elem(int i) { return v[i]; }
};

// 1.13
class vec : public vector {
  int low, high;
public:
  vec(int, int);
  int& elem(int);
  int& operator[](int);
};


main()
{
 vector a(10);
 for (int i=0; i<a.size(); i++) {
     a[i] = i;
     cout << a[i] << " ";
 }
 cout << "\n";
 vec b(10,19);
 for (i=0; i<b.size(); i++) b[i+10] = a[i];
 for (i=0; i<b.size(); i++) cout << b[i+10] << " ";
 cout << "\n";
}

extern void exit(int);
// 1.13
void error(char* p)
{
cerr << p << "\n";
exit (1);
}

// 1.11
vector::vector(int s)
{
  if (s<=0) error("bad vector size");
  sz = s;
  v = new int[s];
}

int& vector::operator[](int i)
{
   if (i<0 || sz<=i) error("vector index out of range");
   return v[i];
}

vector::~vector()
{
  delete v;
}

// 1.13
int& vec::elem(int i)
{
  return vector::elem(i-low);
}

vec::vec(int lb, int hb) : (hb-lb+1)
{
  if (hb-lb<0) hb = lb;
  low = lb;
  high = hb;
}

void vector::set_size(int) { /* dummy */ }

int& vec::operator[](int i)
{
 if (i<low || high<i) error("vec index out of range");
 return elem(i);
}



b1__14.cxx

#include<stream.hxx>

extern void exit( int );
extern void error( char* );

// 1.11
class vector {
  int *v;
  int sz;
public:
       vector(int);   // constructor
       ~vector();    // destructor
  int size() { return sz; }
  void set_size(int);
  int& operator[](int);
  int& elem(int i) { return v[i]; }
};

vector::vector(int s)
{
  if (s<=0) error("bad vector size");
  sz = s;
  v = new int[s];
}

int& vector::operator[](int i)
{
   if (i<0 || sz<=i) error("vector index out of range");
   return v[i];
}

vector::~vector()
{
  delete v;
}

// 1.14
class Vec : public vector {
public:
  Vec(int s) : (s) {}
  Vec(Vec&);
  ~Vec() {}
  void operator=(Vec&);
  void operator*=(Vec&);
  void operator*=(int);
};

Vec::Vec(Vec& a) : (a.size())
{
int sz = a.size();
for (int i = 0; i<sz; i++) elem(i) =a.elem(i);
}

void Vec::operator=(Vec& a)
{
 int s = size();
 if (s!=a.size()) error("bad vector size for =");
 for (int i =0; i<s; i++) elem(i)=a.elem(i);
}

Vec operator+(Vec& a, Vec& b)
{
 int s = a.size();
 if (s != b.size()) error("bad vector size for +");
 Vec sum(s);
 for (int i=0; i<s; i++)
    sum.elem(i) = a.elem(i) + b.elem(i);
 return sum;
}


void error(char* p)
{
cerr << p << "\n";
exit (1);
}

void vector::set_size(int) {  }

main()
{
 Vec a(10);
 Vec b(10);
 for (int i=0; i<a.size(); i++) a[i] = i;
 b = a;
 Vec c = a+b;
 for (i=0; i<c.size(); i++) cout << c[i] << "\n";
}



b1__16.cxx

#include <vector.hxx>

declare(vector,int);
implement(vector,int);

main()
{
  vector(int) vv(10);
  vv[2] = 3;
  vv[10] = 4;		// range error
}



b2_1_3.cxx

#include <stream.hxx>

int a = 1;

void f()
{
  int b = 1;
  static int c = 1;
  cout << " a = " << a++
       << " b = " << b++
       << " c = " << c++ << "\n";
}

main ()
{
 while (a < 4) f();
}



b2_3.cxx

#include <stream.hxx>

main()
{
 int* p = new int;
 cout << "sizeof(int) = " << sizeof(int) "\n";
}



b2_3_6a.cxx

#include <stream.hxx>

extern int strlen(char*);

char alpha[] = "abcdefghijklmnopqrstuvwxyz";

main ()
{
 int sz = strlen(alpha);

 for (int i=0; i<sz; i++) {
     char ch = alpha[i];
     cout << "'" << chr(ch) << "'"
          << " = " << ch
          << " = 0" << oct(ch)
          << " = 0x" << hex(ch) << "\n";
 }
}



b2_3_6b.cxx

#include <stream.hxx>

char v[2][5] = {
   'a', 'b', 'c', 'd', 'e',
   '0', '1', '2', '3', '4'
};

main() {
 for ( int i = 0; i<2; i++) {
     for (int j = 0; j <5; j++)
         cout << "v[" << i << "][" << j
              << "]=" << chr(v[i][j]) << "  ";
    cout << "\n";
}
}



b2_3_7.cxx

#include <stream.hxx>

main()
{
  char cv[10];
  int iv[10];

  char* pc = cv;
  int* pi = iv;

 cout << "char* " << long(pc+1)-long(pc) << "\n";
 cout << "int* "  << long(pi+1)-long(pi) << "\n";
}



b2_3__10.cxx

#include <stream.hxx>

struct pair {
    char* name;
    int val;
};
extern int strlen(char*);
extern int strcpy(char*, char*);
extern int strcmp(char*, char*);

const large = 1024;
static pair vec[large];

pair* find(char* p)
{
 for (int i=0; vec[i].name; i++)
     if (strcmp(p,vec[i].name)==0) return &vec[i];

 if (i== large) return &vec[large-1];

 return &vec[i];
}

int& value(char* p)
{
  pair* res = find(p);
  if (res->name == 0) {
     res->name = new char[strlen(p)+1];
     strcpy(res->name,p);
     res->val = 0;
  }
  return res->val;
}

const MAX = 256;


main ()
{
 char buf [MAX];

 while ( cin>>buf) value(buf)++;

 for (int i=0; vec[i].name; i++)
   cout << vec[i].name << ":" << vec[i].val << "\n";
}



b3_1all.cxx

#include <xstream.hxx>
#include <ctype.h>

enum token_value {
   NAME,  NUMBER, END,
   PLUS = '+',  MINUS = '-',  MUL='*',     DIV='/',
   PRINT=';',    ASSIGN='=',  LP='(',   RP=')'
};

token_value curr_tok;

struct name {
  char* string;
  name* next;
  double value;
};


const TBLSZ = 23;
name* table[TBLSZ];

int no_of_errors;

double error(char* s) {
  cerr << "error: " << s << "\n";
  no_of_errors++;
  return 1;
}

extern int strlen(const char*);
extern int strcmp(const char*, const char*);
extern char* strcpy(char*, const char*);

name* look(char* p, int ins = 0)
{
  int ii= 0;
  char *pp = p;
  while (*pp) ii = ii<<1 ^ *pp++;
  if (ii < 0) ii = -ii;
  ii %= TBLSZ;

  for (name* n=table [ii]; n; n=n->next)
      if (strcmp(p,n->string) == 0) return n;

  if (ins == 0) error("name not found");

  name* nn = new name;
  nn->string = new char[strlen(p) + 1];
  strcpy(nn->string,p);
  nn->value = 1;
  nn->next = table[ii];
  table[ii] = nn;
  return nn;
}

inline name* insert(char* s) { return look (s,1); }

token_value get_token();
double term();

double expr()
{
  double left = term();

  for (;;)
      switch (curr_tok) {
      case PLUS:
           get_token();
           left += term();
           break;
      case MINUS:
           get_token();
           left -= term();
           break;
      default :
           return left;
      }
}

double prim();

double term()
{
  double left = prim();

  for (;;)
      switch (curr_tok) {
      case MUL:
           get_token();
           left *= prim();
           break;
      case DIV:
           get_token();
           double d = prim();
           if (d == 0) return error("divide by o");
           left /= d;
           break;
      default:
           return left;
      }
}
int number_value;
char name_string[80];

double prim()
{
  switch (curr_tok) {
  case NUMBER:
       get_token();
       return number_value;
  case NAME:
       if (get_token() == ASSIGN) {
          name* n = insert(name_string);
          get_token();
          n->value = expr();
          return n->value;
       }
       return look(name_string)->value;
  case MINUS:
       get_token();
       return -prim();
  case LP:
       get_token();
       double e = expr();
       if (curr_tok != RP) return error(") expected");
       get_token();
       return e;
  case END:
       return 1;
  default:
       return error ("primary expected");
  }
}

token_value get_token()
{
 char ch = 0;

 do {
    if(!cin.get(ch)) return curr_tok = END;
 } while (ch!='\n' && isspace(ch));

 switch (ch) {
 case ';':
 case '\n':
      cin >> WS;
      return curr_tok=PRINT;
 case '*':
 case '/':
 case '+':
 case '-':
 case '(':
 case ')':
 case '=':
      return curr_tok=ch;
 case '0': case '1': case '2': case '3': case '4':
 case '5': case '6': case '7': case '8': case '9':
 case '.':
    cin.putback(ch);
    cin >> number_value;
    return curr_tok=NUMBER;
 default:
   if (isalpha(ch)) {
      char* p = name_string;
      *p++ = ch;
      while (cin.get(ch) && isalnum(ch)) *p++ = ch;
      cin.putback(ch);
      *p = 0;
      return curr_tok=NAME;
   }
   error ("bad token");
   return curr_tok=PRINT;
  }
}

int main(int argc, char* argv[])
{
  switch (argc) {
  case 1:
     break;
  case 2:
     cin = *new istream(strlen(argv[1]),argv[1]);
     break;
  default:
     error("too many arguments");
     return 1;
  }

  // insert predefined names:
  insert("pi")->value = 3.1415926535897932385;
  insert("e")->value = 2.7182818284590452354;

  while (1) {
     get_token();
     if( curr_tok == END) break;
     if (curr_tok == PRINT) continue;
     cout << expr() << "\n";
  }

  return no_of_errors;
}



b3_2_6a.cxx

extern void strcpy(char *,char *);
extern void exit(int);
extern int strlen(char *);

char *save_string(char* p)
{
 char* s = new char[strlen(p)+1];
 strcpy(s,p);
 return s;
}

int main (int argc, char* argv[])
{
  if (argc < 2) exit(1);
  int size = strlen(argv[1])+1;
  char* p = save_string (argv[1]);
  delete[size] p;
}



b3_2_6b.cxx

#include <stream.hxx>

extern void exit( int );
void out_of_store()
{

  cout << "operator new failed: out of store\n";
  exit(1);
}

typedef void (*PF)();

extern PF set_new_handler(PF);

main()
{
  set_new_handler(&out_of_store);
  char *p = new char[100000000];
  cout << "done, p = " << long(p) << "\n";
}



b4_6_8.cxx

// This version of the program does not assume sizeof(int)==sizeof(char*) !

#include <stream.hxx>
#include <stdarg.hxx>

extern void exit(int);
void error (int ...);

main(int argc, char* argv[])
{
  switch (argc) {
  case 1:
      error(0,argv[0],(char*)0);
      break;
  case 2:
      error(0,argv[0],argv[1],(char*)0);
      break;
  default :
     error(1,"with",dec(argc-1),"arguments",(char*)0);
  }
}


void error(int n ...)
{
 va_list ap;
 va_start(ap,n);

 for (;;) {
     char *p = va_arg(ap,char*);
     if (p == 0) break;
     cerr << p << " ";
 }

 va_end(ap);

 cerr << "\n";
 if (n) exit(n);
}

b4_6_9.cxx

#include <stream.hxx>

struct user {
   char *name;
   char* id;
   int dept;
};

typedef user* Puser;

user heads[] = {
 "Mcilroy M.D",     "doug", 11271,
 "Aho A.v.",        "ava",  11272,
 "Weinberger P.J.", "pjw",  11273,
 "Schryer N.L.",    "nls",  11274,
 "Schryer N.L.",    "nls",  11275,
 "Kernighan B.W.",  "bwk",  11276
};

typedef int (*CFT)(char*,char*);

void sort(char* base, unsigned n, int sz, CFT cmp)
{
 for (int i=0; i<n-1; i++)
     for (int j=n-1; i<j; j--) {
         char* pj = base+j*sz;
         char *pj1 = pj-sz;
         if ((*cmp)(pj,pj1) < 0)
            // swap b[j] and b[j-1]
           for (int k=0; k<sz; k++) {
               char temp = pj[k];
               pj[k] = pj1[k];
               pj1[k] = temp;
           }
     }
}

void print_id(Puser v, int n)
{
  for (int i=0; i<n; i++)
      cout << v[i].name << "\t"
           << v[i].id   << "\t"
           << v[i].dept << "\n";
}
extern int strcmp(char*, char*);

int cmp1(char* p, char* q)
{
  return strcmp(Puser(p)->name, Puser(q)->name);
}

int cmp2(char* p, char* q)
{
  return Puser(p)->dept - Puser(q)->dept;
}

main ()
{
  sort((char*)heads,6,sizeof(user),cmp1);
  print_id(heads,6);
  cout << "\n";
  sort ((char*)heads,6,sizeof(user),cmp2);
  print_id(heads,6);       // in department number order
}



b5_3_2.cxx


#include <stream.hxx>

class intset {
   int cursize, maxsize;
   int *x;
public:
   intset(int m, int n);
   ~intset();

   int member(int t);
   void insert(int t);

   void iterate(int& i)  { i = 0; }
   int ok(int& i)        { return i<cursize; }
   int next(int& i)      { return x[i++]; }
};

extern void exit (int);

void error(char *s)
{
  cout << "set: " << s << "\n";
  exit(1);
}

extern int atoi(char *);

extern int rand();

int randint (int u)  // in the range 1..u
{
  int r = rand();
  if (r < 0) r = -r;
  return 1 + r%u ;
}

intset::intset(int m, int n)
{
  if (m<1 || n<m) error("illegal intset size");
  cursize = 0;
  maxsize = m;
  x = new int[maxsize];
}

intset::~intset()
{
 delete x;
}

void intset::insert(int t)
{
  if (++cursize > maxsize) error("too many elements");
  int i = cursize-1;
  x[i] = t;

  while (i>0 && x[i-1]>x[i]) {
      int t = x[i];
      x[i] = x[i-1];
      x[i-1] = t;
      i--;
  }
}

int intset::member(int t)
{
  int l = 0;
  int u = cursize-1;

  int m =0;
  while (l <= u) {
      m = (l+u)/2;
      if (t < x[m])
         u = m-1;
      else if (t > x[m])
          l = m+1;
      else
          return 1;    // found
  }
  return 0;    // not found
}

void print_in_order(intset* set)
{
 int var;
 set->iterate(var);
 while (set->ok(var)) cout << set->next(var) << "\n";
}

main (int argc, char *argv[])
{
 if (argc != 3) error("two arguments expected");
 int count = 0;
 int m = atoi(argv[1]);
 int n = atoi (argv[2]);
 intset s(m,n);

 int t = 0;
 while (count <m) {
     t = randint(n);
     if (s.member(t)==0) {
        s.insert(t);
        count++;
    }
 }
 print_in_order(&s);
}



b5_4_5.cxx

#include <stream.hxx>

struct cl
{
  char* val;
  void print(int x) { cout << val << x << "\n"; }
  cl(char *v) { val = v; }
};


typedef void (cl::*PROC)(int);

main()
{
  cl z1("z1 ");
  cl z2("z2 ");
  PROC pf1 = &cl::print;
  PROC pf2 = &cl::print;
  z1.print(1);
  (z1.*pf1)(2);
  z2.print(3);
  ((&z2)->*pf2)(4);
}



b5_5_3.cxx

main() {
 char *p = new char[100];
 char *q = new char[100];
 delete p;
 delete p;
}



b6_3_2.cxx


#include "stream.hxx"

int error (char * p)
{
 cout << p << "\n";
 return 1;
}

class tiny {
  char v;
  tiny assign(int i)
  {  v = (i&~63) ? (error("range error"),0) : i; return *this; }
public:
  tiny (int i)       { assign(i); }
  tiny (tiny& t)      { v = t.v; }
  tiny operator=(tiny& t1) { v = t1.v; return *this; }
  tiny operator=(int i ) { return assign(i); }
  int operator int()         { return v; }
};

void main()
{
 tiny c1 = 2;
 tiny c2 = 62;
 tiny c3 = (c2 - c1);
 tiny c4 = c3;
 int i = (c1 + c2);
 c1 = (c2 + (2 * c1));
 c2 = c1 - i;
 c3 = c2;
}



b6_6.cxx

#include <stream.hxx>

extern int strcpy(char* , char*);

extern int strlen(char *);

struct string {
   char *p;
   int size;
   inline string(int sz) { p = new char[size=sz]; }
   string(char *);
   inline ~string() { delete p; }
   void operator=(string&);
   string(string& );
};

string::string(char* s)
{
 p = new char [size = strlen(s) + 1];
 strcpy (p,s);
}

void string::operator=(string& a)
{
 if (this == &a) return;
 delete p;
 p=new char[size=a.size];
 strcpy(p,a.p);
}


string::string(string& a)
{
 p=new char[size=a.size];
 strcpy(p,a.p);
}
string g(string arg)
{
 return arg;
}

main()
{
 string s = "asdf";
 s = g(s);
 cout << s.p << "\n";
}



b6_7.cxx

#include <stream.hxx>
#include <string.h>
struct pair {
 char * name;
 int val;
};

class assoc {
pair * vec;
int max;
int free;
public:
assoc(int);
int& operator[](char* );
void print_all();
};

assoc::assoc(int s)
{
max = (s<16) ? s: 16;
free = 0;
vec = new pair[max];
}

int& assoc::operator[](char * p)
/*
 maintain a set of "pair"s
 search for p,
 return a reference to the integer part of its "pair"
 make a new "pair" if "p" has not been seen
*/
{
 register pair* pp;
 for (pp=&vec[free-1]; vec<=pp; pp-- )
    if (strcmp(p, pp->name)==0) return pp->val;

 if (free==max) { // overflow: grow the vector
    pair* nvec = new pair[max*2];
    for (int i=0; i<max; i++) nvec[i] = vec[i];
    delete vec;
    vec = nvec;
    max = 2*max;
 }

  pp = &vec[free++];
  pp->name = new char[strlen(p)+1];
  strcpy(pp->name,p);
  pp->val = 0;
  return pp->val;
}

void assoc::print_all()
{
 for (int i=0; i<free; i++)
     cout << vec[i].name << ": " << vec[i].val << "\n";
}

main()
{
 const MAX = 256;
 char buf[MAX];
 assoc vec(512);
 while ( cin>>buf) vec[buf]++;
 vec.print_all();
}



b6_8.cxx


#include <stream.hxx>
#include <string.h>

struct pair {
   char* name;
   int val;
};

class assoc {
friend class assoc_iterator;
   pair* vec;
   int max;
   int free;
public:
   assoc(int);
   int& operator[](char*);
};
class assoc_iterator {
  assoc* cs;
  int i;
public:
  assoc_iterator(assoc& s) { cs = &s; i = 0; }
  pair* operator()()
        { return (i<cs->free)? &cs->vec[i++] : 0; }
};

assoc::assoc(int s)
{
 max = (s<16) ? s : 16;
 free = 0;
 vec = new pair[max];
}

int& assoc::operator[](char* p)
{
 register pair* pp;

 for (pp=&vec[free-1]; vec<=pp; pp-- )
     if (strcmp(p,pp->name)==0) return pp->val;

 if (free ==max) {
    pair* nvec = new pair[max*2];
    for (int i=0; i<max; i++) nvec[i] = vec[i];
    delete vec;
    vec = nvec;
    max = 2*max;
 }

  pp = &vec[free++];
  pp->name = new char[strlen(p)+1];
  strcpy(pp->name,p);
  pp->val = 0;
  return pp->val;
}

main()
{
 const MAX = 256;
 char buf[MAX];
 assoc vec(512);
 while ( cin>>buf) vec[buf]++;
 assoc_iterator next(vec);
 pair* p;
 while (p = next() )
     cout << p->name << ": " << p->val << "\n";
}



b6_9.cxx

#include <stream.hxx>
#include <string.h>

extern void exit(int);
class string {
  struct srep {
       char* s;
       int n;
  };
  srep *p;

public:
  string(char *);
  string();
  string(string &);
  string& operator=(char *);
  string& operator=(string &);
  ~string();
  char& operator[](int i);

  friend ostream& operator<<(ostream&, string&);
  friend istream& operator>> (istream&, string&);

  friend int operator==(string &x, char *s)
      { return strcmp(x.p->s, s) == 0; }

   friend int operator==(string &x, string &y)
      { return strcmp(x.p->s, y.p->s) == 0; }

   friend int operator!=(string &x, char *s)
      { return strcmp(x.p->s, s) != 0; }

   friend int operator!=(string &x, string &y)
      { return strcmp (x.p->s, y.p->s) != 0; }
};

string::string()
{
  p = new srep;
  p->s = 0;
  p->n = 1;
}

string::string(char* s)
{
 p = new srep;
 p->s = new char[ strlen(s) +1];
 strcpy(p->s, s);
 p->n = 1;
}
string::string(string& x)
{
 x.p->n++;
 p = x.p;
}

string::~string()
{
 if (--p->n == 0){
    delete p->s;
    delete p;
 }
}

string& string::operator=(char* s)
{
 if (p->n > 1) {
    p->n--;
    p = new srep;
 }
 else if (p->n == 1)
    delete p->s;

 p->s = new char[ strlen(s)+1 ];
 strcpy(p->s, s);
 p->n = 1;
 return *this;
}

string& string::operator=(string& x)
{
 x.p->n++;
 if (--p->n == 0) {
    delete p->s;
    delete p;
 }
 p = x.p;
 return *this;
}

ostream& operator<<(ostream& s, string& x)
{
  return s << x.p->s << " [" << x.p->n << "]\n";
}

istream& operator>>(istream& s, string& x)
{
 char buf[256];
 s>>buf;
 x = buf;
 cout << "echo: " << x << "\n";
 return s;
}

void error(char* p)
{
 cout << p << "\n";
 exit(1);
}
char& string::operator[](int i)
{
 if (i<0 || strlen(p->s)<i) error("index out of range");
 return p->s[i];
}

main()
{
  string x[100];
  int n;

  cout << "here we go\n";
  for (n = 0; cin>>x[n]; n++) {
      string y;
      if (n==100) error("too many strings");
      cout << (y = x[n]);
      if (y=="done") break;
  }
  cout << "here we go back again\n";
  for (int i=n-1; 0<=i; i--) cout << x[i];
}



b7_2_8.cxx

#include <stream.hxx>

struct employee {
friend class manager;
   employee* next;
   char*     name;
   short     department;
   virtual void print();
};

struct manager : employee {
   employee* group;
   short     level;
   void print();
};

void employee::print()
{
 cout << name << "\t" << department << "\n";
}

void manager::print()
{
  employee::print();
  cout << "\tlevel " << level << "\n";
}

void f(employee* ll)
{
 for ( ; ll; ll=ll->next) ll->print();
}

main ()
{
  employee e;
      e.name = "J. Brown";
      e.department = 1234;
      e.next = 0;
   manager m;
      m.name = "J. Smith";
      m.department = 1234;
      m.level = 2;
      m.next = &e;
f(&m);
}



b7_7.cxx


#include <stream.hxx>

struct base { base(); };

struct derived : base { derived(); };

base:: base()
{
 cout << "\tbase 1: this=" << long(this) << "\n";
 if (this == 0) this = (base*)27;
 cout << "\tbase 2: this=" << long(this) << "\n";
}

derived::derived()
{
 cout << "\tderived 1: this=" << long(this) << "\n";
 if (this == 0) this = (derived*)43;
 cout << "\tderived 2: this=" << long(this) << "\n";
}

main()
{
 cout << "base b;\n";
 base b;
 cout << "new base;\n";
 new base;
 cout  << "derived  d;\n";
 derived d;
 cout << "new derived;\n";
 new derived;
 cout << "new derived;\n";
 new derived;
 cout << "at the end\n";
}



b8_3_3.cxx


#include <xstream.hxx>

extern void exit(int);

void error(char* s, char* s2)
{
 cerr << s << " " << s2 << "\n";
 exit(1);
}

main(int argc, char* argv[])
{
 if (argc != 3) error ("wrong number of arguments","");

 filebuf f1;
 if (f1.open(argv[1],input) == 0)
    error("cannot open input file",argv[1]);
 istream from(&f1);

 filebuf f2;
 if (f2.open(argv[2],output) == 0)
    error("cannot open input file",argv[2]);
 ostream to(&f2);

 char ch;
 while (from.get(ch)) to.put(ch);

 if (!from.eof() || to.bad())
    error("something strange happened","");
}



Популярность: 15, Last-modified: Wed, 08 May 2002 14:07:22 GMT



